From 8f97ae8bbcd4dae8f5c31cf4fd97e4c3c64e5080 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 14 Jun 2023 14:00:50 +0000 Subject: [PATCH] lazy get snapppoints using realized elements in VirtualizingStackPanel --- .../Utils/VirtualizingSnapPointsList.cs | 191 ++++++++++++++++++ .../VirtualizingStackPanel.cs | 103 +--------- 2 files changed, 194 insertions(+), 100 deletions(-) create mode 100644 src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs diff --git a/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs b/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs new file mode 100644 index 0000000000..afa084bd9b --- /dev/null +++ b/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls.Primitives; +using Avalonia.Layout; + +namespace Avalonia.Controls.Utils +{ + internal class VirtualizingSnapPointsList : IReadOnlyList + { + private const int ExtraCount = 2; + private readonly RealizedStackElements _realizedElements; + private readonly Orientation _orientation; + private readonly Orientation _parentOrientation; + private readonly SnapPointsAlignment _snapPointsAlignment; + private readonly double _size; + private readonly int _start = -1; + private readonly int _end; + + public VirtualizingSnapPointsList(RealizedStackElements realizedElements, int count, Orientation orientation, Orientation parentOrientation, SnapPointsAlignment snapPointsAlignment, double size) + { + _realizedElements = realizedElements; + _orientation = orientation; + _parentOrientation = parentOrientation; + _snapPointsAlignment = snapPointsAlignment; + _size = size; + if (parentOrientation == orientation) + { + _start = Math.Max(0, _realizedElements.FirstIndex - ExtraCount); + _end = Math.Min(count - 1, _realizedElements.LastIndex + ExtraCount); + } + } + + public double this[int index] + { + get + { + if(index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index"); + + index += _start; + + double snapPoint = 0; + var averageElementSize = _size; + + Control? container; + switch (_orientation) + { + case Orientation.Horizontal: + container = _realizedElements.GetElement(index); + if (container != null) + { + switch (_snapPointsAlignment) + { + case SnapPointsAlignment.Near: + snapPoint = container.Bounds.Left; + break; + case SnapPointsAlignment.Center: + snapPoint = container.Bounds.Center.X; + break; + case SnapPointsAlignment.Far: + snapPoint = container.Bounds.Right; + break; + } + } + else + { + var ind = index; + if (index > _realizedElements.LastIndex) + { + ind -= _realizedElements.LastIndex + 1; + } + snapPoint = ind * averageElementSize; + switch (_snapPointsAlignment) + { + case SnapPointsAlignment.Center: + snapPoint += averageElementSize / 2; + break; + case SnapPointsAlignment.Far: + snapPoint += averageElementSize; + break; + } + if (index > _realizedElements.LastIndex) + { + var lastElement = _realizedElements.GetElement(_realizedElements.LastIndex); + if (lastElement != null) + { + snapPoint += lastElement.Bounds.Right; + } + } + } + break; + case Orientation.Vertical: + container = _realizedElements.GetElement(index); + if (container != null) + { + switch (_snapPointsAlignment) + { + case SnapPointsAlignment.Near: + snapPoint = container.Bounds.Top; + break; + case SnapPointsAlignment.Center: + snapPoint = container.Bounds.Center.Y; + break; + case SnapPointsAlignment.Far: + snapPoint = container.Bounds.Bottom; + break; + } + } + else + { + var ind = index; + if(index > _realizedElements.LastIndex) + { + ind -= _realizedElements.LastIndex + 1; + } + snapPoint = ind * averageElementSize; + switch (_snapPointsAlignment) + { + case SnapPointsAlignment.Center: + snapPoint += averageElementSize / 2; + break; + case SnapPointsAlignment.Far: + snapPoint += averageElementSize; + break; + } + if (index > _realizedElements.LastIndex) + { + var lastElement = _realizedElements.GetElement(_realizedElements.LastIndex); + if (lastElement != null) + { + snapPoint += lastElement.Bounds.Bottom; + } + } + } + break; + } + + return snapPoint; + } + } + + public int Count => _parentOrientation != _orientation ? 0 : _end - _start + 1; + + public IEnumerator GetEnumerator() + { + return new VirtualizingEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new VirtualizingEnumerator(this); + } + + private class VirtualizingEnumerator : IEnumerator + { + private readonly VirtualizingSnapPointsList _list; + private int _index; + + public VirtualizingEnumerator(VirtualizingSnapPointsList list) + { + _list = list; + _index = -1; + } + + public double Current => _list[_index]; + + object IEnumerator.Current => Current; + + public void Dispose() + { + _index = 0; + } + + public bool MoveNext() + { + _index++; + + return _index < _list.Count; + } + + public void Reset() + { + _index = -1; + } + } + } +} diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index c262d416be..6a3c7e17ee 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -776,107 +776,10 @@ namespace Avalonia.Controls /// public IReadOnlyList GetIrregularSnapPoints(Orientation orientation, SnapPointsAlignment snapPointsAlignment) { - var snapPoints = new List(); - var itemCount = ItemsControl?.ItemsSource?.Count() ?? 0; - switch (orientation) - { - case Orientation.Horizontal: - if (AreHorizontalSnapPointsRegular) - throw new InvalidOperationException(); - if (Orientation == Orientation.Horizontal) - { - var averageElementSize = EstimateElementSizeU(); - double snapPoint = 0; - for (var i = 0; i < itemCount; i++) - { - var container = ContainerFromIndex(i); - if (container != null) - { - switch (snapPointsAlignment) - { - case SnapPointsAlignment.Near: - snapPoint = container.Bounds.Left; - break; - case SnapPointsAlignment.Center: - snapPoint = container.Bounds.Center.X; - break; - case SnapPointsAlignment.Far: - snapPoint = container.Bounds.Right; - break; - } - } - else - { - if (snapPoint == 0) - { - switch (snapPointsAlignment) - { - case SnapPointsAlignment.Center: - snapPoint = averageElementSize / 2; - break; - case SnapPointsAlignment.Far: - snapPoint = averageElementSize; - break; - } - } - else - snapPoint += averageElementSize; - } - - snapPoints.Add(snapPoint); - } - } - break; - case Orientation.Vertical: - if (AreVerticalSnapPointsRegular) - throw new InvalidOperationException(); - if (Orientation == Orientation.Vertical) - { - var averageElementSize = EstimateElementSizeU(); - double snapPoint = 0; - for (var i = 0; i < itemCount; i++) - { - var container = ContainerFromIndex(i); - if (container != null) - { - switch (snapPointsAlignment) - { - case SnapPointsAlignment.Near: - snapPoint = container.Bounds.Top; - break; - case SnapPointsAlignment.Center: - snapPoint = container.Bounds.Center.Y; - break; - case SnapPointsAlignment.Far: - snapPoint = container.Bounds.Bottom; - break; - } - } - else - { - if (snapPoint == 0) - { - switch (snapPointsAlignment) - { - case SnapPointsAlignment.Center: - snapPoint = averageElementSize / 2; - break; - case SnapPointsAlignment.Far: - snapPoint = averageElementSize; - break; - } - } - else - snapPoint += averageElementSize; - } - - snapPoints.Add(snapPoint); - } - } - break; - } + if(_realizedElements == null) + return new List(); - return snapPoints; + return new VirtualizingSnapPointsList(_realizedElements, ItemsControl?.ItemsSource?.Count() ?? 0, orientation, Orientation, snapPointsAlignment, EstimateElementSizeU()); } ///