|
|
|
@ -1,7 +1,6 @@ |
|
|
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
|
|
|
|
using Avalonia.Controls.Primitives; |
|
|
|
using System; |
|
|
|
using System.Collections.Specialized; |
|
|
|
|
|
|
|
@ -11,6 +10,9 @@ namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
private double _takenSpace; |
|
|
|
private int _canBeRemoved; |
|
|
|
private double _averageItemSize; |
|
|
|
private int _averageCount; |
|
|
|
private double _pixelOffset; |
|
|
|
|
|
|
|
bool IVirtualizingPanel.IsFull |
|
|
|
{ |
|
|
|
@ -24,12 +26,30 @@ namespace Avalonia.Controls |
|
|
|
|
|
|
|
int IVirtualizingPanel.OverflowCount => _canBeRemoved; |
|
|
|
|
|
|
|
double IVirtualizingPanel.AverageItemSize => _averageItemSize; |
|
|
|
|
|
|
|
double IVirtualizingPanel.PixelOffset |
|
|
|
{ |
|
|
|
get { return _pixelOffset; } |
|
|
|
|
|
|
|
set |
|
|
|
{ |
|
|
|
if (_pixelOffset != value) |
|
|
|
{ |
|
|
|
_pixelOffset = value; |
|
|
|
InvalidateArrange(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Action IVirtualizingPanel.ArrangeCompleted { get; set; } |
|
|
|
|
|
|
|
protected override Size ArrangeOverride(Size finalSize) |
|
|
|
{ |
|
|
|
_canBeRemoved = 0; |
|
|
|
_takenSpace = 0; |
|
|
|
_averageItemSize = 0; |
|
|
|
_averageCount = 0; |
|
|
|
var result = base.ArrangeOverride(finalSize); |
|
|
|
((IVirtualizingPanel)this).ArrangeCompleted?.Invoke(); |
|
|
|
return result; |
|
|
|
@ -44,7 +64,7 @@ namespace Avalonia.Controls |
|
|
|
case NotifyCollectionChangedAction.Add: |
|
|
|
foreach (IControl control in e.NewItems) |
|
|
|
{ |
|
|
|
UpdatePhysicalSizeForAdd(control); |
|
|
|
UpdateAdd(control); |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
@ -52,7 +72,7 @@ namespace Avalonia.Controls |
|
|
|
case NotifyCollectionChangedAction.Remove: |
|
|
|
foreach (IControl control in e.OldItems) |
|
|
|
{ |
|
|
|
UpdatePhysicalSizeForRemove(control); |
|
|
|
UpdateRemove(control); |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
@ -65,64 +85,93 @@ namespace Avalonia.Controls |
|
|
|
Size panelSize, |
|
|
|
Orientation orientation) |
|
|
|
{ |
|
|
|
base.ArrangeChild(child, rect, panelSize, orientation); |
|
|
|
|
|
|
|
if (orientation == Orientation.Horizontal) |
|
|
|
if (orientation == Orientation.Vertical) |
|
|
|
{ |
|
|
|
if (rect.X >= panelSize.Width) |
|
|
|
rect = new Rect(rect.X, rect.Y - _pixelOffset, rect.Width, rect.Height); |
|
|
|
child.Arrange(rect); |
|
|
|
|
|
|
|
if (rect.Y >= panelSize.Height) |
|
|
|
{ |
|
|
|
++_canBeRemoved; |
|
|
|
} |
|
|
|
|
|
|
|
if (rect.Right >= _takenSpace) |
|
|
|
if (rect.Bottom >= _takenSpace) |
|
|
|
{ |
|
|
|
_takenSpace = rect.Right; |
|
|
|
_takenSpace = rect.Bottom; |
|
|
|
} |
|
|
|
|
|
|
|
AddToAverageItemSize(rect.Height); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if (rect.Y >= panelSize.Height) |
|
|
|
rect = new Rect(rect.X - _pixelOffset, rect.Y, rect.Width, rect.Height); |
|
|
|
child.Arrange(rect); |
|
|
|
|
|
|
|
if (rect.X >= panelSize.Width) |
|
|
|
{ |
|
|
|
++_canBeRemoved; |
|
|
|
} |
|
|
|
|
|
|
|
if (rect.Bottom >= _takenSpace) |
|
|
|
if (rect.Right >= _takenSpace) |
|
|
|
{ |
|
|
|
_takenSpace = rect.Bottom; |
|
|
|
_takenSpace = rect.Right; |
|
|
|
} |
|
|
|
|
|
|
|
AddToAverageItemSize(rect.Width); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void UpdatePhysicalSizeForAdd(IControl child) |
|
|
|
private void UpdateAdd(IControl child) |
|
|
|
{ |
|
|
|
var bounds = Bounds; |
|
|
|
var gap = Gap; |
|
|
|
|
|
|
|
child.Measure(bounds.Size); |
|
|
|
++_averageCount; |
|
|
|
|
|
|
|
if (Orientation == Orientation.Vertical) |
|
|
|
{ |
|
|
|
_takenSpace += child.DesiredSize.Height + gap; |
|
|
|
var height = child.DesiredSize.Height; |
|
|
|
_takenSpace += height + gap; |
|
|
|
AddToAverageItemSize(height); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
_takenSpace += child.DesiredSize.Width + gap; |
|
|
|
var width = child.DesiredSize.Width; |
|
|
|
_takenSpace += width + gap; |
|
|
|
AddToAverageItemSize(width); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void UpdatePhysicalSizeForRemove(IControl child) |
|
|
|
private void UpdateRemove(IControl child) |
|
|
|
{ |
|
|
|
var bounds = Bounds; |
|
|
|
var gap = Gap; |
|
|
|
|
|
|
|
if (Orientation == Orientation.Vertical) |
|
|
|
{ |
|
|
|
_takenSpace -= child.DesiredSize.Height + gap; |
|
|
|
var height = child.DesiredSize.Height; |
|
|
|
_takenSpace -= height + gap; |
|
|
|
RemoveFromAverageItemSize(height); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
_takenSpace -= child.DesiredSize.Width + gap; |
|
|
|
var width = child.DesiredSize.Width; |
|
|
|
_takenSpace -= width + gap; |
|
|
|
RemoveFromAverageItemSize(width); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void AddToAverageItemSize(double value) |
|
|
|
{ |
|
|
|
++_averageCount; |
|
|
|
_averageItemSize += (value - _averageItemSize) / _averageCount; |
|
|
|
} |
|
|
|
|
|
|
|
private void RemoveFromAverageItemSize(double value) |
|
|
|
{ |
|
|
|
_averageItemSize = ((_averageItemSize * _averageCount) - value) / (_averageCount - 1); |
|
|
|
--_averageCount; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|