diff --git a/src/Avalonia.Controls/Utils/RealizedWrappedElements.cs b/src/Avalonia.Controls/Utils/RealizedWrappedElements.cs index a94296715a..610bc3d698 100644 --- a/src/Avalonia.Controls/Utils/RealizedWrappedElements.cs +++ b/src/Avalonia.Controls/Utils/RealizedWrappedElements.cs @@ -13,7 +13,7 @@ namespace Avalonia.Controls.Utils { private int _firstIndex; private List? _elements; - private List? _sizes; + private UVSize _size; private List? _positions; private UVSize _startUV; private bool _startUUnstable; @@ -38,11 +38,6 @@ namespace Avalonia.Controls.Utils /// public IReadOnlyList Elements => _elements ??= new List(); - /// - /// Gets the sizes of the elements. - /// - public IReadOnlyList SizeUV => _sizes ??= new List(); - /// /// Gets the positions of the elements. /// @@ -53,12 +48,18 @@ namespace Avalonia.Controls.Utils /// public UVSize StartUV => _startUV; + /// + /// The size of the first realized element. + /// + public UVSize SizeUV => _size; + + /// /// Adds a newly realized element to the collection. /// /// The index of the element. /// The element. - /// The position of the elemnt. + /// The position of the element. /// The size of the element. public void Add(int index, Control element, UVSize uv, UVSize sizeUV) { @@ -66,14 +67,13 @@ namespace Avalonia.Controls.Utils throw new ArgumentOutOfRangeException(nameof(index)); _elements ??= new List(); - _sizes ??= new List(); _positions ??= new List(); var size = sizeUV; if (Count == 0) { _elements.Add(element); - _sizes.Add(size); + _size = size; _positions.Add(uv); _startUV = uv; _firstIndex = index; @@ -81,14 +81,14 @@ namespace Avalonia.Controls.Utils else if (index == LastIndex + 1) { _elements.Add(element); - _sizes.Add(size); + _size = size; _positions.Add(uv); } else if (index == FirstIndex - 1) { --_firstIndex; _elements.Insert(0, element); - _sizes.Insert(0, size); + _size = size; _positions.Insert(0, uv); _startUV = uv; } @@ -143,12 +143,12 @@ namespace Avalonia.Controls.Utils if (MathUtilities.IsZero(viewportStart.U) && MathUtilities.IsZero(viewportStart.V)) return (0, new UVSize(viewportStart.Orientation)); - if (_positions is not null && _sizes is not null && !_startUUnstable) + if (_positions is not null && !_startUUnstable) { for (var i = 0; i < _positions.Count; ++i) { var position = _positions[i]; - var size = _sizes[i]; + var size = _size; if (position.IsNaN) break; @@ -240,32 +240,7 @@ namespace Avalonia.Controls.Utils /// public UVSize? EstimateElementSize(Orientation orientation) { - var divisor = 0.0; - var u = 0.0; - var v = 0.0; - - // Average the size of the realized elements. - if (_sizes is not null) - { - foreach (var size in _sizes) - { - if (size.IsNaN) - continue; - u += size.U; - v += size.V; - ++divisor; - } - } - - // We don't have any elements on which to base our estimate. - if (divisor == 0 || u == 0 || v == 0) - return null; - - return new UVSize(orientation) - { - U = u / divisor, - V = v / divisor - }; + return _size.IsNaN ? null : _size; } /// @@ -320,7 +295,7 @@ namespace Avalonia.Controls.Utils // The insertion point was within the realized elements, insert an empty space // in _elements and _sizes. _elements!.InsertMany(realizedIndex, null, count); - _sizes!.InsertMany(realizedIndex, new UVSize(Orientation.Horizontal, double.NaN, double.NaN), count); + _size = new UVSize(Orientation.Horizontal, double.NaN, double.NaN); _positions!.InsertMany(realizedIndex, new UVSize(Orientation.Horizontal, double.NaN, double.NaN), count); } } @@ -381,7 +356,6 @@ namespace Avalonia.Controls.Utils } _elements.RemoveRange(start, end - start); - _sizes!.RemoveRange(start, end - start); _positions!.RemoveRange(start, end - start); // If the remove started before and ended within our realized elements, then our new @@ -409,6 +383,7 @@ namespace Avalonia.Controls.Utils /// Recycles all elements in response to the source collection being reset. /// /// A method used to recycle elements. + /// The orientation of the WrapPanel public void ItemsReset(Action recycleElement, Orientation orientation) { if (_elements is null || _elements.Count == 0) @@ -426,7 +401,7 @@ namespace Avalonia.Controls.Utils _firstIndex = 0; _startUV = new UVSize(orientation); _elements?.Clear(); - _sizes?.Clear(); + _size = new UVSize(orientation); _positions?.Clear(); } @@ -458,7 +433,6 @@ namespace Avalonia.Controls.Utils } _elements.RemoveRange(0, endIndex); - _sizes!.RemoveRange(0, endIndex); _positions!.RemoveRange(0, endIndex); _firstIndex = index; } @@ -469,6 +443,7 @@ namespace Avalonia.Controls.Utils /// /// The index in the source collection of new last element. /// A method used to recycle elements. + /// The orientation of the WrapPanel public void RecycleElementsAfter(int index, Action recycleElement, Orientation orientation) { if (index >= LastIndex || _elements is null || _elements.Count == 0) @@ -493,7 +468,6 @@ namespace Avalonia.Controls.Utils } _elements.RemoveRange(startIndex, _elements.Count - startIndex); - _sizes!.RemoveRange(startIndex, _sizes.Count - startIndex); _positions!.RemoveRange(startIndex, _positions.Count - startIndex); } } @@ -502,6 +476,7 @@ namespace Avalonia.Controls.Utils /// Recycles all realized elements. /// /// A method used to recycle elements. + /// The orientation of the WrapPanel public void RecycleAllElements(Action recycleElement, Orientation orientation) { if (_elements is null || _elements.Count == 0) @@ -519,20 +494,21 @@ namespace Avalonia.Controls.Utils _firstIndex = 0; _startUV = new UVSize(orientation); _elements?.Clear(); - _sizes?.Clear(); + _size = new UVSize(orientation); _positions?.Clear(); } /// /// Resets the element list and prepares it for reuse. /// + /// The orientation of the WrapPanel public void ResetForReuse(Orientation orientation) { _firstIndex = 0; _startUV = new UVSize(orientation); _startUUnstable = false; _elements?.Clear(); - _sizes?.Clear(); + _size = new UVSize(orientation); _positions?.Clear(); } } diff --git a/src/Avalonia.Controls/VirtualizingWrapPanel.cs b/src/Avalonia.Controls/VirtualizingWrapPanel.cs index 66e32275d0..c2e89c5cac 100644 --- a/src/Avalonia.Controls/VirtualizingWrapPanel.cs +++ b/src/Avalonia.Controls/VirtualizingWrapPanel.cs @@ -175,7 +175,7 @@ namespace Avalonia.Controls if (e is not null) { - var sizeUV = _realizedElements.SizeUV[i]; + var sizeUV = _realizedElements.SizeUV; var positionUV = _realizedElements.PositionsUV[i]; var rect = new Rect(positionUV.Width, positionUV.Height, sizeUV.Width, sizeUV.Height); e.Arrange(rect); @@ -335,7 +335,7 @@ namespace Avalonia.Controls var viewport = _viewport != s_invalidViewport ? _viewport : EstimateViewport(); var viewportEnd = Orientation == Orientation.Horizontal ? new UVSize(Orientation, viewport.Right, viewport.Bottom) : new UVSize(Orientation, viewport.Bottom, viewport.Right); - // Get the expected position of the elment and put it in place. + // Get the expected position of the element and put it in place. var anchorUV = _realizedElements.GetOrEstimateElementUV(index, ref _lastEstimatedElementSizeUV, viewportEnd); size = new Size(isItemWidthSet ? itemWidth : _scrollToElement.DesiredSize.Width, isItemHeightSet ? itemHeight : _scrollToElement.DesiredSize.Height); @@ -529,6 +529,7 @@ namespace Avalonia.Controls var v = uv.V; double maxSizeV = 0; var size = new UVSize(Orientation); + bool firstChildMeasured = false; double itemWidth = ItemWidth; double itemHeight = ItemHeight; @@ -552,12 +553,20 @@ namespace Avalonia.Controls break; } + if (firstChildMeasured) + childConstraint = new Size(size.Width, size.Height); + var e = GetOrCreateElement(items, index); e.Measure(childConstraint); - size = new UVSize(Orientation, - isItemWidthSet ? itemWidth : e.DesiredSize.Width, - isItemHeightSet ? itemHeight : e.DesiredSize.Height); + if (!firstChildMeasured) + { + size = new UVSize(Orientation, + isItemWidthSet ? itemWidth : e.DesiredSize.Width, + isItemHeightSet ? itemHeight : e.DesiredSize.Height); + + firstChildMeasured = true; + } maxSizeV = Math.Max(maxSizeV, size.V); @@ -607,12 +616,22 @@ namespace Avalonia.Controls break; } + if (firstChildMeasured) + childConstraint = new Size(size.Width, size.Height); + var e = GetOrCreateElement(items, index); e.Measure(childConstraint); - size = new UVSize(Orientation, - isItemWidthSet ? itemWidth : e.DesiredSize.Width, - isItemHeightSet ? itemHeight : e.DesiredSize.Height); + + if (!firstChildMeasured) + { + size = new UVSize(Orientation, + isItemWidthSet ? itemWidth : e.DesiredSize.Width, + isItemHeightSet ? itemHeight : e.DesiredSize.Height); + + firstChildMeasured = true; + } + uv.U -= size.U; // Test if the item will be moved to the previous row