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 positions = new(); private readonly List 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)); } } }