@ -2,6 +2,7 @@
using System.Collections.Generic ;
using System.Collections.Generic ;
using System.Collections.Specialized ;
using System.Collections.Specialized ;
using System.Diagnostics ;
using System.Diagnostics ;
using System.Drawing ;
using System.Linq ;
using System.Linq ;
using Avalonia.Controls.Primitives ;
using Avalonia.Controls.Primitives ;
using Avalonia.Controls.Utils ;
using Avalonia.Controls.Utils ;
@ -159,6 +160,7 @@ namespace Avalonia.Controls
try
try
{
{
_ realizedElements ? . ValidateStartU ( Orientation ) ;
_ realizedElements ? ? = new ( ) ;
_ realizedElements ? ? = new ( ) ;
_ measureElements ? ? = new ( ) ;
_ measureElements ? ? = new ( ) ;
@ -179,6 +181,10 @@ namespace Avalonia.Controls
( _ measureElements , _ realizedElements ) = ( _ realizedElements , _ measureElements ) ;
( _ measureElements , _ realizedElements ) = ( _ realizedElements , _ measureElements ) ;
_ measureElements . ResetForReuse ( ) ;
_ measureElements . ResetForReuse ( ) ;
// If there is a focused element is outside the visible viewport (i.e.
// _focusedElement is non-null), ensure it's measured.
_f ocusedElement ? . Measure ( availableSize ) ;
return CalculateDesiredSize ( orientation , items . Count , viewport ) ;
return CalculateDesiredSize ( orientation , items . Count , viewport ) ;
}
}
finally
finally
@ -215,6 +221,16 @@ namespace Avalonia.Controls
}
}
}
}
// Ensure that the focused element is in the correct position.
if ( _f ocusedElement is not null & & _f ocusedIndex > = 0 )
{
u = GetOrEstimateElementU ( _f ocusedIndex ) ;
var rect = orientation = = Orientation . Horizontal ?
new Rect ( u , 0 , _f ocusedElement . DesiredSize . Width , finalSize . Height ) :
new Rect ( 0 , u , finalSize . Width , _f ocusedElement . DesiredSize . Height ) ;
_f ocusedElement . Arrange ( rect ) ;
}
return finalSize ;
return finalSize ;
}
}
finally
finally
@ -389,7 +405,7 @@ namespace Avalonia.Controls
scrollToElement . Measure ( Size . Infinity ) ;
scrollToElement . Measure ( Size . Infinity ) ;
// Get the expected position of the element and put it in place.
// Get the expected position of the element and put it in place.
var anchorU = _ realizedElements . GetOrEstimateElementU ( index , ref _l astEstimatedElementSizeU ) ;
var anchorU = GetOrEstimateElementU ( index ) ;
var rect = Orientation = = Orientation . Horizontal ?
var rect = Orientation = = Orientation . Horizontal ?
new Rect ( anchorU , 0 , scrollToElement . DesiredSize . Width , scrollToElement . DesiredSize . Height ) :
new Rect ( anchorU , 0 , scrollToElement . DesiredSize . Width , scrollToElement . DesiredSize . Height ) :
new Rect ( 0 , anchorU , scrollToElement . DesiredSize . Width , scrollToElement . DesiredSize . Height ) ;
new Rect ( 0 , anchorU , scrollToElement . DesiredSize . Width , scrollToElement . DesiredSize . Height ) ;
@ -472,11 +488,12 @@ namespace Avalonia.Controls
}
}
else
else
{
{
( anchorIndex , anchorU ) = _ realizedElements . GetOrEstimateAnchorElementForViewport (
GetOrEstimateAnchorElementForViewport (
viewportStart ,
viewportStart ,
viewportEnd ,
viewportEnd ,
items . Count ,
items . Count ,
ref _l astEstimatedElementSizeU ) ;
out anchorIndex ,
out anchorU ) ;
}
}
// Check if the anchor element is not within the currently realized elements.
// Check if the anchor element is not within the currently realized elements.
@ -531,12 +548,98 @@ namespace Avalonia.Controls
if ( _ realizedElements is null )
if ( _ realizedElements is null )
return _l astEstimatedElementSizeU ;
return _l astEstimatedElementSizeU ;
var result = _ realizedElements . EstimateElementSizeU ( ) ;
var orientation = Orientation ;
if ( result > = 0 )
var total = 0.0 ;
_l astEstimatedElementSizeU = result ;
var divisor = 0.0 ;
return _l astEstimatedElementSizeU ;
// Average the desired size of the realized, measured elements.
foreach ( var element in _ realizedElements . Elements )
{
if ( element is null | | ! element . IsMeasureValid )
continue ;
var sizeU = orientation = = Orientation . Horizontal ?
element . DesiredSize . Width :
element . DesiredSize . Height ;
total + = sizeU ;
+ + divisor ;
}
// Check we have enough information on which to base our estimate.
if ( divisor = = 0 | | total = = 0 )
return _l astEstimatedElementSizeU ;
// Store and return the estimate.
return _l astEstimatedElementSizeU = total / divisor ;
}
private void GetOrEstimateAnchorElementForViewport (
double viewportStartU ,
double viewportEndU ,
int itemCount ,
out int index ,
out double position )
{
// We have no elements, or we're at the start of the viewport.
if ( itemCount < = 0 | | MathUtilities . IsZero ( viewportStartU ) )
{
index = 0 ;
position = 0 ;
return ;
}
// If we have realised elements and a valid StartU then try to use this information to
// get the anchor element.
if ( _ realizedElements ? . StartU is { } u & & ! double . IsNaN ( u ) )
{
var orientation = Orientation ;
for ( var i = 0 ; i < _ realizedElements . Elements . Count ; + + i )
{
if ( _ realizedElements . Elements [ i ] is not { } element )
continue ;
var sizeU = orientation = = Orientation . Horizontal ?
element . DesiredSize . Width :
element . DesiredSize . Height ;
var endU = u + sizeU ;
if ( endU > viewportStartU & & u < viewportEndU )
{
index = _ realizedElements . FirstIndex + i ;
position = u ;
return ;
}
u = endU ;
}
}
// We don't have any realized elements in the requested viewport, or can't rely on
// StartU being valid. Estimate the index using only the estimated element size.
var estimatedSize = EstimateElementSizeU ( ) ;
// Estimate the element at the start of the viewport.
var startIndex = Math . Min ( ( int ) ( viewportStartU / estimatedSize ) , itemCount - 1 ) ;
index = startIndex ;
position = startIndex * estimatedSize ;
}
}
private double GetOrEstimateElementU ( int index )
{
// Return the position of the existing element if realized.
var u = _ realizedElements ? . GetElementU ( index ) ? ? double . NaN ;
if ( ! double . IsNaN ( u ) )
return u ;
// Estimate the element size.
var estimatedSize = EstimateElementSizeU ( ) ;
// TODO: Use _startU to work this out.
return index * estimatedSize ;
}
private void RealizeElements (
private void RealizeElements (
IReadOnlyList < object? > items ,
IReadOnlyList < object? > items ,
Size availableSize ,
Size availableSize ,