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: 1889780345}
|
||||||
- component: {fileID: 1889780344}
|
- component: {fileID: 1889780344}
|
||||||
- component: {fileID: 1889780343}
|
- component: {fileID: 1889780343}
|
||||||
|
- component: {fileID: 1889780346}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Main Camera
|
m_Name: Main Camera
|
||||||
m_TagString: MainCamera
|
m_TagString: MainCamera
|
||||||
|
@ -397,6 +398,50 @@ Transform:
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 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
|
--- !u!1660057539 &9223372036854775807
|
||||||
SceneRoots:
|
SceneRoots:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
@ -5,55 +6,15 @@ using UnityEngine.UIElements;
|
||||||
|
|
||||||
namespace KitsuneCafe.UI
|
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 DefaultItemSize { get; set; }
|
||||||
public float GutterSize { get; set; }
|
public float GutterSize { get; set; }
|
||||||
public FlowDirection Direction { get; set; }
|
public FlowDirection Direction { get; set; }
|
||||||
public float ContentSize { get; private 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> positions = new();
|
||||||
private readonly List<float> sizes = new();
|
private readonly List<float> sizes = new();
|
||||||
|
@ -105,6 +66,7 @@ namespace KitsuneCafe.UI
|
||||||
sizes.RemoveRange(itemCount, sizes.Count - itemCount);
|
sizes.RemoveRange(itemCount, sizes.Count - itemCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Count = itemCount;
|
||||||
ContentSize = LastPositionOrDefault();
|
ContentSize = LastPositionOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +74,7 @@ namespace KitsuneCafe.UI
|
||||||
{
|
{
|
||||||
var position = initialPosition;
|
var position = initialPosition;
|
||||||
|
|
||||||
for (int i = startingIndex; i < positions.Count; i++)
|
for (int i = startingIndex; i < Count; i++)
|
||||||
{
|
{
|
||||||
positions[i] = position;
|
positions[i] = position;
|
||||||
position += GetItemSize(i) + GutterSize;
|
position += GetItemSize(i) + GutterSize;
|
||||||
|
@ -134,13 +96,13 @@ namespace KitsuneCafe.UI
|
||||||
CalculatePositions(index, position);
|
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;
|
return i;
|
||||||
}
|
}
|
||||||
|
@ -149,28 +111,29 @@ namespace KitsuneCafe.UI
|
||||||
return positions.Count - 1;
|
return positions.Count - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<LayoutItem> Enumerate(int startIndex = 0)
|
public Range GetVisibleRange(float offset, float size)
|
||||||
{
|
{
|
||||||
var len = positions.Count;
|
var halfBuffer = Mathf.CeilToInt(Buffer / 2);
|
||||||
for (int i = startIndex; i < len; i++)
|
|
||||||
|
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()
|
count += halfBuffer;
|
||||||
{
|
|
||||||
return new LayoutEnumerator(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<LayoutItem> GetEnumerator(int startIndex)
|
return new Range(first, Math.Min(Count, first + count));
|
||||||
{
|
|
||||||
return new LayoutEnumerator(startIndex, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
namespace KitsuneCafe.UI
|
namespace KitsuneCafe.UI
|
||||||
|
@ -10,6 +12,8 @@ namespace KitsuneCafe.UI
|
||||||
|
|
||||||
public float ItemSize { get; set; }
|
public float ItemSize { get; set; }
|
||||||
public float GutterSize { 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)
|
public FixedLayout(FlowDirection direction, float itemSize, float gutterSize)
|
||||||
{
|
{
|
||||||
|
@ -32,7 +36,18 @@ namespace KitsuneCafe.UI
|
||||||
|
|
||||||
public void Update(int itemCount, VisualElement _container)
|
public void Update(int itemCount, VisualElement _container)
|
||||||
{
|
{
|
||||||
|
Count = itemCount;
|
||||||
ContentSize = itemCount * (ItemSize + GutterSize);
|
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;
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
namespace KitsuneCafe.UI
|
namespace KitsuneCafe.UI
|
||||||
|
@ -6,9 +8,11 @@ namespace KitsuneCafe.UI
|
||||||
{
|
{
|
||||||
FlowDirection Direction { get; }
|
FlowDirection Direction { get; }
|
||||||
float ContentSize { get; }
|
float ContentSize { get; }
|
||||||
|
int Buffer { get; }
|
||||||
|
|
||||||
float GetItemPosition(int index);
|
float GetItemPosition(int index);
|
||||||
float GetItemSize(int index);
|
float GetItemSize(int index);
|
||||||
|
Range GetVisibleRange(float offset, float size);
|
||||||
void Update(int itemCount, VisualElement container);
|
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]
|
[CreateProperty]
|
||||||
public ICollectionDataSource DataSource
|
public ICollectionDataSource DataSource
|
||||||
{
|
{
|
||||||
|
@ -152,8 +199,8 @@ namespace KitsuneCafe.UI
|
||||||
{
|
{
|
||||||
return isDynamicSize switch
|
return isDynamicSize switch
|
||||||
{
|
{
|
||||||
true => new DynamicLayout(Direction, itemSize),
|
true => new DynamicLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter },
|
||||||
false => new FixedLayout(Direction, itemSize)
|
false => new FixedLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,9 +71,6 @@ namespace KitsuneCafe.UI
|
||||||
private readonly BiDictionary<int, VisualElement> visibleItems = new();
|
private readonly BiDictionary<int, VisualElement> visibleItems = new();
|
||||||
private float parentSize = 0;
|
private float parentSize = 0;
|
||||||
private float lastKnownScrollOffset = 0;
|
private float lastKnownScrollOffset = 0;
|
||||||
|
|
||||||
private readonly int itemCountBuffer = 2;
|
|
||||||
|
|
||||||
private readonly Subject<UpdateRequest> requestSubject = new();
|
private readonly Subject<UpdateRequest> requestSubject = new();
|
||||||
private IDisposable subscriptions;
|
private IDisposable subscriptions;
|
||||||
|
|
||||||
|
@ -102,16 +99,6 @@ namespace KitsuneCafe.UI
|
||||||
});
|
});
|
||||||
|
|
||||||
subscriptions = Disposable.Combine(full, partial);
|
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);
|
Layout.Update(DataSource.Length, Container);
|
||||||
ScheduleUpdate();
|
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()
|
private void UpdateVisibleItems()
|
||||||
{
|
{
|
||||||
if (DataSource == null || DataSource.Length == 0 || Mathf.Approximately(parentSize, 0))
|
if (DataSource == null || DataSource.Length == 0 || Mathf.Approximately(parentSize, 0))
|
||||||
|
@ -272,15 +232,9 @@ namespace KitsuneCafe.UI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var scrollOffset = lastKnownScrollOffset;
|
var scrollOffset = lastKnownScrollOffset;
|
||||||
|
|
||||||
var range = Layout switch
|
var range = layout.GetVisibleRange(scrollOffset, parentSize);
|
||||||
{
|
|
||||||
FixedLayout fl => GetFixedItems(fl, scrollOffset),
|
|
||||||
DynamicLayout dl => GetDynamicItems(dl, scrollOffset),
|
|
||||||
_ => throw new NotImplementedException(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var firstIndex = range.Start.Value;
|
var firstIndex = range.Start.Value;
|
||||||
var lastIndex = range.End.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">
|
<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>
|
</ui:UXML>
|
||||||
|
|
Loading…
Add table
Reference in a new issue