250 lines
6.4 KiB
C#
250 lines
6.4 KiB
C#
using System;
|
|
using Unity.Properties;
|
|
using UnityEditor.UIElements;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace KitsuneCafe.UI
|
|
{
|
|
public enum FlowDirection
|
|
{
|
|
Vertical = 0,
|
|
Horizontal = 1
|
|
}
|
|
|
|
|
|
public abstract record CollectionItemSize;
|
|
|
|
[Serializable]
|
|
public record FixedItemSize(float Size) : CollectionItemSize;
|
|
|
|
[Serializable]
|
|
public record DynamicItemSize() : CollectionItemSize
|
|
{
|
|
public static readonly DynamicItemSize Instance = new();
|
|
}
|
|
|
|
public class CollectionItemSizeConverter : UxmlAttributeConverter<CollectionItemSize>
|
|
{
|
|
public override CollectionItemSize FromString(string value)
|
|
{
|
|
return value switch
|
|
{
|
|
"()" => new DynamicItemSize(),
|
|
var v => new FixedItemSize(int.Parse(v))
|
|
};
|
|
}
|
|
|
|
public override string ToString(CollectionItemSize value)
|
|
{
|
|
return value switch
|
|
{
|
|
DynamicItemSize => "()",
|
|
var v => v.ToString(),
|
|
};
|
|
}
|
|
}
|
|
|
|
[UxmlElement]
|
|
public partial class RecycleView : VisualElement, IDisposable
|
|
{
|
|
public const string RecycleViewBaseClass = "kitsunecafe__recycle-view";
|
|
public const string RecycleViewScrollViewClass = "kitsunecafe__recycle-view--scroll-view";
|
|
public const string RecycleViewContentContainerClass = "kitsunecafe__recycle-view--content-container";
|
|
public const string RecycleViewDynamicItemClass = "kitsunecafe__recycle-view--dynamic-item";
|
|
|
|
|
|
private FlowDirection direction;
|
|
|
|
[UxmlAttribute, CreateProperty]
|
|
public FlowDirection Direction
|
|
{
|
|
get => direction;
|
|
set
|
|
{
|
|
if (direction != value)
|
|
{
|
|
direction = value;
|
|
virtualizationController.Layout = CreateLayout();
|
|
UpdateDirection();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private bool isDynamicSize = false;
|
|
|
|
[UxmlAttribute, CreateProperty]
|
|
public bool IsDynamicSize
|
|
{
|
|
get => isDynamicSize;
|
|
set
|
|
{
|
|
if (isDynamicSize != value)
|
|
{
|
|
isDynamicSize = value;
|
|
virtualizationController.Layout = CreateLayout();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private float itemSize = 22;
|
|
|
|
[UxmlAttribute, CreateProperty, Delayed, Tooltip("In a dynamic layout, this is used for initial calculations")]
|
|
public float ItemSize
|
|
{
|
|
get => itemSize;
|
|
set
|
|
{
|
|
if (itemSize != value)
|
|
{
|
|
itemSize = value;
|
|
if (virtualizationController.Layout is FixedLayout fixedLayout)
|
|
{
|
|
fixedLayout.ItemSize = value;
|
|
}
|
|
else if (virtualizationController.Layout is DynamicLayout dynamicLayout)
|
|
{
|
|
dynamicLayout.DefaultItemSize = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private int bufferCount = 8;
|
|
|
|
[UxmlAttribute, CreateProperty, Delayed]
|
|
public int BufferCount
|
|
{
|
|
get => bufferCount;
|
|
set
|
|
{
|
|
if (bufferCount != value)
|
|
{
|
|
bufferCount = value;
|
|
if (virtualizationController.Layout is FixedLayout fixedLayout)
|
|
{
|
|
fixedLayout.Buffer = value;
|
|
}
|
|
else if (virtualizationController.Layout is DynamicLayout dynamicLayout)
|
|
{
|
|
dynamicLayout.Buffer = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private float gutter = 0;
|
|
|
|
[UxmlAttribute, CreateProperty, Delayed]
|
|
public float Gutter
|
|
{
|
|
get => gutter;
|
|
set
|
|
{
|
|
if (gutter != value)
|
|
{
|
|
gutter = value;
|
|
if (virtualizationController.Layout is FixedLayout fixedLayout)
|
|
{
|
|
fixedLayout.GutterSize = value;
|
|
}
|
|
else if (virtualizationController.Layout is DynamicLayout dynamicLayout)
|
|
{
|
|
dynamicLayout.GutterSize = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[CreateProperty]
|
|
public ICollectionDataSource DataSource
|
|
{
|
|
get => virtualizationController?.DataSource;
|
|
set
|
|
{
|
|
if (virtualizationController != null)
|
|
{
|
|
virtualizationController.DataSource = value;
|
|
virtualizationController.Setup();
|
|
}
|
|
}
|
|
}
|
|
|
|
private readonly ScrollView scrollView;
|
|
public override VisualElement contentContainer => scrollView.contentContainer;
|
|
|
|
private readonly IRecycleVirtualizationContainer virtualizationController;
|
|
|
|
public RecycleView()
|
|
{
|
|
AddToClassList(RecycleViewBaseClass);
|
|
|
|
scrollView = new ScrollView();
|
|
scrollView.AddToClassList(RecycleViewScrollViewClass);
|
|
hierarchy.Add(scrollView);
|
|
contentContainer.AddToClassList(RecycleViewContentContainerClass);
|
|
|
|
virtualizationController = new RecycleVirtualizationController
|
|
{
|
|
Container = contentContainer
|
|
};
|
|
|
|
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
|
}
|
|
|
|
private ILayout CreateLayout()
|
|
{
|
|
return isDynamicSize switch
|
|
{
|
|
true => new DynamicLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter },
|
|
false => new FixedLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter }
|
|
};
|
|
}
|
|
|
|
|
|
private void UpdateDirection()
|
|
{
|
|
switch (direction)
|
|
{
|
|
case FlowDirection.Vertical:
|
|
scrollView.mode = ScrollViewMode.Vertical;
|
|
scrollView.verticalScrollerVisibility = ScrollerVisibility.Auto;
|
|
scrollView.horizontalScrollerVisibility = ScrollerVisibility.Hidden;
|
|
scrollView.horizontalScroller.valueChanged -= virtualizationController.OnScrolled;
|
|
scrollView.verticalScroller.valueChanged += virtualizationController.OnScrolled;
|
|
break;
|
|
case FlowDirection.Horizontal:
|
|
scrollView.mode = ScrollViewMode.Horizontal;
|
|
scrollView.verticalScrollerVisibility = ScrollerVisibility.Hidden;
|
|
scrollView.horizontalScrollerVisibility = ScrollerVisibility.Auto;
|
|
scrollView.verticalScroller.valueChanged -= virtualizationController.OnScrolled;
|
|
scrollView.horizontalScroller.valueChanged += virtualizationController.OnScrolled;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void OnGeometryChanged(GeometryChangedEvent evt)
|
|
{
|
|
if (evt.newRect.size != evt.oldRect.size && virtualizationController != null)
|
|
{
|
|
virtualizationController.OnParentSizeChanged(evt.newRect.size);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (virtualizationController is IDisposable disposable)
|
|
{
|
|
disposable.Dispose();
|
|
}
|
|
}
|
|
|
|
~RecycleView()
|
|
{
|
|
Dispose();
|
|
}
|
|
}
|
|
}
|