configurable buffers and gutters
This commit is contained in:
parent
9e6e3ccdee
commit
843e0220c2
7 changed files with 144 additions and 116 deletions
|
@ -316,6 +316,7 @@ GameObject:
|
|||
- component: {fileID: 1889780345}
|
||||
- component: {fileID: 1889780344}
|
||||
- component: {fileID: 1889780343}
|
||||
- component: {fileID: 1889780346}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
|
@ -397,6 +398,50 @@ Transform:
|
|||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1889780346
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1889780342}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_VolumeFrameworkUpdateModeOption: 2
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_AllowHDROutput: 1
|
||||
m_UseScreenCoordOverride: 0
|
||||
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_Version: 2
|
||||
m_TaaSettings:
|
||||
m_Quality: 3
|
||||
m_FrameInfluence: 0.1
|
||||
m_JitterScale: 1
|
||||
m_MipBias: 0
|
||||
m_VarianceClampScale: 0.9
|
||||
m_ContrastAdaptiveSharpening: 0
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
@ -5,55 +6,15 @@ using UnityEngine.UIElements;
|
|||
|
||||
namespace KitsuneCafe.UI
|
||||
{
|
||||
public record LayoutItem(float Position, float Size)
|
||||
|
||||
public class DynamicLayout : ILayout
|
||||
{
|
||||
public float Min => Position;
|
||||
public float Max => Position + Size;
|
||||
}
|
||||
|
||||
public class DynamicLayout : ILayout, IEnumerable<LayoutItem>
|
||||
{
|
||||
|
||||
public struct LayoutEnumerator : IEnumerator<LayoutItem>
|
||||
{
|
||||
public readonly int Start;
|
||||
public int Index;
|
||||
private readonly DynamicLayout layout;
|
||||
|
||||
public LayoutEnumerator(int index, DynamicLayout layout)
|
||||
{
|
||||
Start = index;
|
||||
Index = index;
|
||||
this.layout = layout;
|
||||
}
|
||||
|
||||
public LayoutEnumerator(DynamicLayout layout) : this(0, layout) { }
|
||||
|
||||
public readonly LayoutItem Current => new(
|
||||
layout.positions[Index - 1],
|
||||
layout.sizes[Index - 1]
|
||||
);
|
||||
|
||||
readonly object IEnumerator.Current => Current;
|
||||
|
||||
public readonly void Dispose() { }
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
Index += 1;
|
||||
return Index < layout.positions.Count;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Index = Start;
|
||||
}
|
||||
}
|
||||
|
||||
public float DefaultItemSize { get; set; }
|
||||
public float GutterSize { get; set; }
|
||||
public FlowDirection Direction { get; set; }
|
||||
public float ContentSize { get; private set; }
|
||||
public int Count { get; set; }
|
||||
public int Buffer { get; set; }
|
||||
|
||||
private readonly List<float> positions = new();
|
||||
private readonly List<float> sizes = new();
|
||||
|
@ -105,6 +66,7 @@ namespace KitsuneCafe.UI
|
|||
sizes.RemoveRange(itemCount, sizes.Count - itemCount);
|
||||
}
|
||||
|
||||
Count = itemCount;
|
||||
ContentSize = LastPositionOrDefault();
|
||||
}
|
||||
|
||||
|
@ -112,7 +74,7 @@ namespace KitsuneCafe.UI
|
|||
{
|
||||
var position = initialPosition;
|
||||
|
||||
for (int i = startingIndex; i < positions.Count; i++)
|
||||
for (int i = startingIndex; i < Count; i++)
|
||||
{
|
||||
positions[i] = position;
|
||||
position += GetItemSize(i) + GutterSize;
|
||||
|
@ -134,13 +96,13 @@ namespace KitsuneCafe.UI
|
|||
CalculatePositions(index, position);
|
||||
}
|
||||
|
||||
public int GetFirstVisibleIndex(float scrollOffset)
|
||||
public int GetFirstVisibleIndex(float offset)
|
||||
{
|
||||
if (scrollOffset <= 0) { return 0; }
|
||||
if (offset <= 0) { return 0; }
|
||||
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (positions[i] >= scrollOffset)
|
||||
if (positions[i] >= offset)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
@ -149,28 +111,29 @@ namespace KitsuneCafe.UI
|
|||
return positions.Count - 1;
|
||||
}
|
||||
|
||||
public IEnumerable<LayoutItem> Enumerate(int startIndex = 0)
|
||||
public Range GetVisibleRange(float offset, float size)
|
||||
{
|
||||
var len = positions.Count;
|
||||
for (int i = startIndex; i < len; i++)
|
||||
var halfBuffer = Mathf.CeilToInt(Buffer / 2);
|
||||
|
||||
var first = Math.Max(
|
||||
GetFirstVisibleIndex(offset) - halfBuffer,
|
||||
0
|
||||
);
|
||||
|
||||
var count = 1;
|
||||
var position = GetItemPosition(first);
|
||||
var width = position + size;
|
||||
|
||||
for (int i = first; i < Count; i++)
|
||||
{
|
||||
yield return new LayoutItem(positions[i], sizes[i]);
|
||||
var item = GetItemPosition(i);
|
||||
if (item > width) { break; }
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<LayoutItem> GetEnumerator()
|
||||
{
|
||||
return new LayoutEnumerator(this);
|
||||
}
|
||||
count += halfBuffer;
|
||||
|
||||
public IEnumerator<LayoutItem> GetEnumerator(int startIndex)
|
||||
{
|
||||
return new LayoutEnumerator(startIndex, this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
return new Range(first, Math.Min(Count, first + count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace KitsuneCafe.UI
|
||||
|
@ -10,6 +12,8 @@ namespace KitsuneCafe.UI
|
|||
|
||||
public float ItemSize { get; set; }
|
||||
public float GutterSize { get; set; }
|
||||
public int Count { get; set; }
|
||||
public int Buffer { get; set; }
|
||||
|
||||
public FixedLayout(FlowDirection direction, float itemSize, float gutterSize)
|
||||
{
|
||||
|
@ -32,7 +36,18 @@ namespace KitsuneCafe.UI
|
|||
|
||||
public void Update(int itemCount, VisualElement _container)
|
||||
{
|
||||
Count = itemCount;
|
||||
ContentSize = itemCount * (ItemSize + GutterSize);
|
||||
}
|
||||
|
||||
public Range GetVisibleRange(float offset, float containerSize)
|
||||
{
|
||||
var size = ItemSize + GutterSize;
|
||||
var first = Mathf.FloorToInt(offset / (ItemSize + GutterSize));
|
||||
var count = Mathf.CeilToInt(containerSize / size) + Buffer;
|
||||
var last = Math.Min(Count, first + count);
|
||||
|
||||
return new Range(first, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace KitsuneCafe.UI
|
||||
|
@ -6,9 +8,11 @@ namespace KitsuneCafe.UI
|
|||
{
|
||||
FlowDirection Direction { get; }
|
||||
float ContentSize { get; }
|
||||
int Buffer { get; }
|
||||
|
||||
float GetItemPosition(int index);
|
||||
float GetItemSize(int index);
|
||||
Range GetVisibleRange(float offset, float size);
|
||||
void Update(int itemCount, VisualElement container);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,53 @@ namespace KitsuneCafe.UI
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -152,8 +199,8 @@ namespace KitsuneCafe.UI
|
|||
{
|
||||
return isDynamicSize switch
|
||||
{
|
||||
true => new DynamicLayout(Direction, itemSize),
|
||||
false => new FixedLayout(Direction, itemSize)
|
||||
true => new DynamicLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter },
|
||||
false => new FixedLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -71,9 +71,6 @@ namespace KitsuneCafe.UI
|
|||
private readonly BiDictionary<int, VisualElement> visibleItems = new();
|
||||
private float parentSize = 0;
|
||||
private float lastKnownScrollOffset = 0;
|
||||
|
||||
private readonly int itemCountBuffer = 2;
|
||||
|
||||
private readonly Subject<UpdateRequest> requestSubject = new();
|
||||
private IDisposable subscriptions;
|
||||
|
||||
|
@ -102,16 +99,6 @@ namespace KitsuneCafe.UI
|
|||
});
|
||||
|
||||
subscriptions = Disposable.Combine(full, partial);
|
||||
// subscriptions = Observable.Merge(full, partial)
|
||||
// // var updateSubscription = requestSubject.ThrottleLastFrame(1)
|
||||
// .Subscribe(request =>
|
||||
// {
|
||||
// if (request is PartialUpdate req)
|
||||
// {
|
||||
// UpdateFromIndex(req.Index);
|
||||
// }
|
||||
// else { UpdateVisibleItems(); }
|
||||
// });
|
||||
|
||||
Layout.Update(DataSource.Length, Container);
|
||||
ScheduleUpdate();
|
||||
|
@ -238,33 +225,6 @@ namespace KitsuneCafe.UI
|
|||
}
|
||||
}
|
||||
|
||||
private Range GetDynamicItems(DynamicLayout layout, float scrollOffset)
|
||||
{
|
||||
var first = layout.GetFirstVisibleIndex(scrollOffset);
|
||||
var count = 1;
|
||||
var position = layout.GetItemPosition(first);
|
||||
var width = position + parentSize;
|
||||
|
||||
foreach (var item in layout.Enumerate(first))
|
||||
{
|
||||
if (item.Position > width) { break; }
|
||||
count += 1;
|
||||
position = item.Position;
|
||||
}
|
||||
|
||||
return new Range(first, Math.Min(DataSource.Length, first + count));
|
||||
}
|
||||
|
||||
private Range GetFixedItems(FixedLayout layout, float scrollOffset)
|
||||
{
|
||||
var size = layout.ItemSize + layout.GutterSize;
|
||||
var first = Mathf.FloorToInt(scrollOffset / (layout.ItemSize + layout.GutterSize));
|
||||
var count = Mathf.CeilToInt(parentSize / size) + itemCountBuffer;
|
||||
var last = Math.Min(DataSource.Length, first + count);
|
||||
|
||||
return new Range(first, last);
|
||||
}
|
||||
|
||||
private void UpdateVisibleItems()
|
||||
{
|
||||
if (DataSource == null || DataSource.Length == 0 || Mathf.Approximately(parentSize, 0))
|
||||
|
@ -272,15 +232,9 @@ namespace KitsuneCafe.UI
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
var scrollOffset = lastKnownScrollOffset;
|
||||
|
||||
var range = Layout switch
|
||||
{
|
||||
FixedLayout fl => GetFixedItems(fl, scrollOffset),
|
||||
DynamicLayout dl => GetDynamicItems(dl, scrollOffset),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
var range = layout.GetVisibleRange(scrollOffset, parentSize);
|
||||
|
||||
var firstIndex = range.Start.Value;
|
||||
var lastIndex = range.End.Value;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<ui:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
|
||||
<KitsuneCafe.UI.RecycleView name="recycle-view" direction="Horizontal" a="Stupid" is-dynamic-size="true" default-item-size="50" fixed-item-size="22" item-size="22" style="flex-grow: 1; flex-direction: row;" />
|
||||
<KitsuneCafe.UI.RecycleView name="recycle-view" direction="Horizontal" a="Stupid" is-dynamic-size="true" default-item-size="50" fixed-item-size="22" gutter="0" style="flex-grow: 1; flex-direction: row;" />
|
||||
</ui:UXML>
|
||||
|
|
Loading…
Add table
Reference in a new issue