139 lines
3.2 KiB
C#
139 lines
3.2 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace KitsuneCafe.UI
|
|
{
|
|
|
|
public class DynamicLayout : ILayout
|
|
{
|
|
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();
|
|
|
|
public DynamicLayout(FlowDirection direction, float defaultItemSize = 22, float gutterSize = 0)
|
|
{
|
|
Direction = direction;
|
|
GutterSize = gutterSize;
|
|
DefaultItemSize = defaultItemSize;
|
|
}
|
|
|
|
public float GetItemPosition(int index)
|
|
{
|
|
if (index > 0)
|
|
{
|
|
return GetItemPosition(index - 1) + GetItemSize(index - 1) + GutterSize;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public float GetItemSize(int index)
|
|
{
|
|
if (index >= 0 && index < sizes.Count)
|
|
{
|
|
return sizes[index];
|
|
}
|
|
|
|
return DefaultItemSize;
|
|
}
|
|
|
|
private float LastPositionOrDefault()
|
|
{
|
|
return positions.Count > 0 ? positions[^1] : DefaultItemSize + GutterSize;
|
|
}
|
|
|
|
public void Update(int itemCount, VisualElement container)
|
|
{
|
|
while (positions.Count < itemCount)
|
|
{
|
|
var last = LastPositionOrDefault();
|
|
positions.Add(last + DefaultItemSize + GutterSize);
|
|
sizes.Add(DefaultItemSize);
|
|
}
|
|
|
|
if (positions.Count > itemCount)
|
|
{
|
|
positions.RemoveRange(itemCount, positions.Count - itemCount);
|
|
sizes.RemoveRange(itemCount, sizes.Count - itemCount);
|
|
}
|
|
|
|
Count = itemCount;
|
|
ContentSize = LastPositionOrDefault();
|
|
}
|
|
|
|
private float CalculatePositions(int startingIndex = 0, float initialPosition = 0)
|
|
{
|
|
var position = initialPosition;
|
|
|
|
for (int i = startingIndex; i < Count; i++)
|
|
{
|
|
positions[i] = position;
|
|
position += GetItemSize(i) + GutterSize;
|
|
}
|
|
|
|
return ContentSize = position;
|
|
}
|
|
|
|
public void SetMeasuredItemSize(int index, float size)
|
|
{
|
|
if (index < sizes.Count && Mathf.Approximately(sizes[index], size))
|
|
{
|
|
return;
|
|
}
|
|
|
|
sizes[index] = size;
|
|
|
|
var position = index > 0 ? positions[index - 1] + sizes[index - 1] + GutterSize : 0;
|
|
CalculatePositions(index, position);
|
|
}
|
|
|
|
public int GetFirstVisibleIndex(float offset)
|
|
{
|
|
if (offset <= 0) { return 0; }
|
|
|
|
for (int i = 0; i < Count; i++)
|
|
{
|
|
if (positions[i] >= offset)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return positions.Count - 1;
|
|
}
|
|
|
|
public Range GetVisibleRange(float offset, float size)
|
|
{
|
|
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++)
|
|
{
|
|
var item = GetItemPosition(i);
|
|
if (item > width) { break; }
|
|
count += 1;
|
|
}
|
|
|
|
count += halfBuffer;
|
|
|
|
return new Range(first, Math.Min(Count, first + count));
|
|
}
|
|
}
|
|
}
|