@ -56,7 +56,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="CacheLength"/> property.
/// </summary>
public static readonly StyledProperty < double > CacheLengthProperty =
AvaloniaProperty . Register < VirtualizingStackPanel , double > ( nameof ( CacheLength ) , 0.0 ,
AvaloniaProperty . Register < VirtualizingStackPanel , double > ( nameof ( CacheLength ) , 0.0 ,
validate : v = > v is > = 0 and < = 2 ) ;
private static readonly AttachedProperty < object? > RecycleKeyProperty =
@ -80,8 +80,8 @@ namespace Avalonia.Controls
private int _f ocusedIndex = - 1 ;
private Control ? _ realizingElement ;
private int _ realizingIndex = - 1 ;
private double _ bufferFactor ;
private double _ bufferFactor ;
private bool _ hasReachedStart = false ;
private bool _ hasReachedEnd = false ;
private Rect _l astMeasuredExtendedViewport ;
@ -270,7 +270,7 @@ namespace Avalonia.Controls
new Rect ( 0 , u , finalSize . Width , sizeU ) ;
e . Arrange ( rect ) ;
if ( e . IsVisible & & _ viewport . Intersects ( rect ) )
{
try
@ -285,7 +285,7 @@ namespace Avalonia.Controls
e , ex . Message ) ;
}
}
u + = orientation = = Orientation . Horizontal ? rect . Width : rect . Height ;
}
}
@ -543,7 +543,7 @@ namespace Avalonia.Controls
var count = Items . Count ;
var fromControl = from as Control ;
if ( count = = 0 | |
if ( count = = 0 | |
( fromControl is null & & direction is not NavigationDirection . First and not NavigationDirection . Last ) )
return null ;
@ -644,7 +644,7 @@ namespace Avalonia.Controls
element . BringIntoView ( ) ;
return element ;
}
else if ( this . GetLayoutRoot ( ) is { } root )
else if ( this . GetLayoutRoot ( ) is { } root )
{
// Create and measure the element to be brought into view. Store it in a field so that
// it can be re-used in the layout pass.
@ -746,7 +746,7 @@ namespace Avalonia.Controls
}
// Check if the anchor element is not within the currently realized elements.
var disjunct = anchorIndex < _ realizedElements . FirstIndex | |
var disjunct = anchorIndex < _ realizedElements . FirstIndex | |
anchorIndex > _ realizedElements . LastIndex ;
return new MeasureViewport
@ -780,12 +780,12 @@ namespace Avalonia.Controls
// We have an element to scroll to, so we can estimate the desired size based on the
// element's position and the remaining elements.
var remaining = itemCount - _ scrollToIndex - 1 ;
var u = orientation = = Orientation . Horizontal ?
var u = orientation = = Orientation . Horizontal ?
_ scrollToElement . Bounds . Right :
_ scrollToElement . Bounds . Bottom ;
var sizeU = u + ( remaining * _l astEstimatedElementSizeU ) ;
return orientation = = Orientation . Horizontal ?
new ( sizeU , DesiredSize . Height ) :
return orientation = = Orientation . Horizontal ?
new ( sizeU , DesiredSize . Height ) :
new ( DesiredSize . Width , sizeU ) ;
}
@ -901,23 +901,26 @@ namespace Avalonia.Controls
{
var first = realized . FirstIndex ;
var last = realized . LastIndex ;
if ( index < first )
{
return realized . StartU - ( ( first - index ) * estimatedSize ) ;
// Interpolate between the known panel origin and the first realized item.
// Using the realized items' average size to extrapolate backwards can place
// the target before the panel origin.
return realized . StartU * index / first ;
}
if ( index > last )
{
var sizes = realized . SizeU ;
var realizedSpan = 0.0 ;
for ( var i = 0 ; i < sizes . Count ; + + i )
{
var sizeU = sizes [ i ] ;
realizedSpan + = double . IsNaN ( sizeU ) ? estimatedSize : sizeU ;
}
return realized . StartU + realizedSpan + ( ( index - last - 1 ) * estimatedSize ) ;
}
}
@ -940,7 +943,7 @@ namespace Avalonia.Controls
var viewportEnd = horizontal ? _ viewport . Right : _ viewport . Bottom ;
var anchorAtEnd = ! _ hasReachedEnd & & index = = items . Count - 1 & &
MathUtilities . GreaterThanOrClose ( viewportEnd , horizontal ? Bounds . Width : Bounds . Height ) ;
// Reset boundary flags
_ hasReachedStart = false ;
_ hasReachedEnd = false ;
@ -956,9 +959,9 @@ namespace Avalonia.Controls
_ realizingIndex = index ;
var e = GetOrCreateElement ( items , index ) ;
_ realizingElement = e ;
e . Measure ( availableSize ) ;
var sizeU = horizontal ? e . DesiredSize . Width : e . DesiredSize . Height ;
var sizeV = horizontal ? e . DesiredSize . Height : e . DesiredSize . Width ;
@ -976,10 +979,10 @@ namespace Avalonia.Controls
_ realizingIndex = - 1 ;
_ realizingElement = null ;
} while ( u < viewport . viewportUEnd & & index < items . Count ) ;
// Check if we reached the end of the collection
_ hasReachedEnd = index > = items . Count ;
// Store the last index and end U position for the desired size calculation.
viewport . lastIndex = index - 1 ;
viewport . realizedEndU = u ;
@ -994,7 +997,7 @@ namespace Avalonia.Controls
while ( u > viewport . viewportUStart & & index > = 0 )
{
var e = GetOrCreateElement ( items , index ) ;
e . Measure ( availableSize ) ;
var sizeU = horizontal ? e . DesiredSize . Width : e . DesiredSize . Height ;
var sizeV = horizontal ? e . DesiredSize . Height : e . DesiredSize . Width ;
@ -1004,7 +1007,7 @@ namespace Avalonia.Controls
viewport . measuredV = Math . Max ( viewport . measuredV , sizeV ) ;
- - index ;
}
// Check if we reached the start of the collection
_ hasReachedStart = index < 0 ;
@ -1039,7 +1042,7 @@ namespace Avalonia.Controls
{
return _ realizedElements ? . GetElement ( index ) ;
}
private static Control ? GetRealizedElement (
int index ,
ref int specialIndex ,
@ -1118,7 +1121,7 @@ namespace Avalonia.Controls
{
Debug . Assert ( ItemsControl is not null ) ;
Debug . Assert ( ItemContainerGenerator is not null ) ;
_ scrollAnchorProvider ? . UnregisterAnchorCandidate ( element ) ;
var recycleKey = element . GetValue ( RecycleKeyProperty ) ;
@ -1153,7 +1156,7 @@ namespace Avalonia.Controls
_ scrollAnchorProvider ? . UnregisterAnchorCandidate ( element ) ;
var recycleKey = element . GetValue ( RecycleKeyProperty ) ;
if ( recycleKey is null )
{
ItemContainerGenerator ! . ClearItemContainer ( element ) ;
@ -1181,7 +1184,7 @@ namespace Avalonia.Controls
_f ocusedElement = null ;
_f ocusedIndex = - 1 ;
}
private void RecycleScrollToElement ( )
{
if ( _ scrollToElement ! = null )
@ -1191,7 +1194,7 @@ namespace Avalonia.Controls
_ scrollToElement = null ;
_ scrollToIndex = - 1 ;
}
private void PushToRecyclePool ( object recycleKey , Control element )
{
_ recyclePool ? ? = new ( ) ;
@ -1211,7 +1214,7 @@ namespace Avalonia.Controls
ItemContainerGenerator . ItemContainerIndexChanged ( element , oldIndex , newIndex ) ;
}
private Rect CalculateExtendedViewport ( bool vertical , double viewportSize , double bufferSize )
{
@ -1378,7 +1381,7 @@ namespace Avalonia.Controls
private void OnItemsControlPropertyChanged ( object? sender , AvaloniaPropertyChangedEventArgs e )
{
if ( _f ocusedElement is not null & &
e . Property = = KeyboardNavigation . TabOnceActiveElementProperty & &
e . Property = = KeyboardNavigation . TabOnceActiveElementProperty & &
e . GetOldValue < IInputElement ? > ( ) = = _f ocusedElement )
{
// TabOnceActiveElement has moved away from _focusedElement so we can recycle it.
@ -1392,15 +1395,15 @@ namespace Avalonia.Controls
{
var newValue = e . GetNewValue < double > ( ) ;
_ bufferFactor = newValue ;
// Force a recalculation of the extended viewport on the next layout pass
InvalidateMeasure ( ) ;
}
/// <inheritdoc/>
public IReadOnlyList < double > GetIrregularSnapPoints ( Orientation orientation , SnapPointsAlignment snapPointsAlignment )
{
if ( _ realizedElements = = null )
if ( _ realizedElements = = null )
return new List < double > ( ) ;
return new VirtualizingSnapPointsList ( _ realizedElements , ItemsControl ? . ItemsSource ? . Count ( ) ? ? 0 , orientation , Orientation , snapPointsAlignment , EstimateElementSizeU ( ) ) ;