Browse Source

Merge pull request #7210 from AvaloniaUI/fixes/avalonia-layout-nullability

Added nullable annotations to Avalonia.Layout.
release/0.10.11
Steven Kirk 4 years ago
committed by Dan Walmsley
parent
commit
f8b399257e
  1. 6
      src/Avalonia.Layout/AttachedLayout.cs
  2. 1
      src/Avalonia.Layout/Avalonia.Layout.csproj
  3. 46
      src/Avalonia.Layout/ElementManager.cs
  4. 68
      src/Avalonia.Layout/FlowLayoutAlgorithm.cs
  5. 4
      src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs
  6. 4
      src/Avalonia.Layout/LayoutContext.cs
  7. 2
      src/Avalonia.Layout/LayoutContextAdapter.cs
  8. 2
      src/Avalonia.Layout/LayoutHelper.cs
  9. 1
      src/Avalonia.Layout/LayoutQueue.cs
  10. 2
      src/Avalonia.Layout/Layoutable.cs
  11. 4
      src/Avalonia.Layout/NonVirtualizingLayoutContext.cs
  12. 22
      src/Avalonia.Layout/StackLayout.cs
  13. 26
      src/Avalonia.Layout/UniformGridLayout.cs
  14. 2
      src/Avalonia.Layout/UniformGridLayoutState.cs
  15. 5
      src/Avalonia.Layout/Utils/ListUtils.cs
  16. 4
      src/Avalonia.Layout/VirtualLayoutContextAdapter.cs
  17. 2
      src/Avalonia.Layout/VirtualizingLayoutContext.cs
  18. 2
      src/Avalonia.Layout/WrapLayout/UvMeasure.cs
  19. 2
      src/Avalonia.Layout/WrapLayout/WrapItem.cs
  20. 6
      src/Avalonia.Layout/WrapLayout/WrapLayout.cs
  21. 9
      src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs

6
src/Avalonia.Layout/AttachedLayout.cs

@ -12,17 +12,17 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public abstract class AttachedLayout : AvaloniaObject public abstract class AttachedLayout : AvaloniaObject
{ {
public string LayoutId { get; set; } public string? LayoutId { get; set; }
/// <summary> /// <summary>
/// Occurs when the measurement state (layout) has been invalidated. /// Occurs when the measurement state (layout) has been invalidated.
/// </summary> /// </summary>
public event EventHandler MeasureInvalidated; public event EventHandler? MeasureInvalidated;
/// <summary> /// <summary>
/// Occurs when the arrange state (layout) has been invalidated. /// Occurs when the arrange state (layout) has been invalidated.
/// </summary> /// </summary>
public event EventHandler ArrangeInvalidated; public event EventHandler? ArrangeInvalidated;
/// <summary> /// <summary>
/// Initializes any per-container state the layout requires when it is attached to an /// Initializes any per-container state the layout requires when it is attached to an

1
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -9,4 +9,5 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" /> <Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project> </Project>

46
src/Avalonia.Layout/ElementManager.cs

@ -13,10 +13,10 @@ namespace Avalonia.Layout
{ {
internal class ElementManager internal class ElementManager
{ {
private readonly List<ILayoutable> _realizedElements = new List<ILayoutable>(); private readonly List<ILayoutable?> _realizedElements = new List<ILayoutable?>();
private readonly List<Rect> _realizedElementLayoutBounds = new List<Rect>(); private readonly List<Rect> _realizedElementLayoutBounds = new List<Rect>();
private int _firstRealizedDataIndex; private int _firstRealizedDataIndex;
private VirtualizingLayoutContext _context; private VirtualizingLayoutContext? _context;
private bool IsVirtualizingContext private bool IsVirtualizingContext
{ {
@ -58,7 +58,7 @@ namespace Avalonia.Layout
// Make sure there is enough space for the bounds. // Make sure there is enough space for the bounds.
// Note: We could optimize when the count becomes smaller, but keeping // Note: We could optimize when the count becomes smaller, but keeping
// it always up to date is the simplest option for now. // it always up to date is the simplest option for now.
_realizedElementLayoutBounds.Resize(count); _realizedElementLayoutBounds.Resize(count, default);
} }
} }
} }
@ -66,34 +66,32 @@ namespace Avalonia.Layout
public int GetRealizedElementCount() public int GetRealizedElementCount()
{ {
return IsVirtualizingContext ? _realizedElements.Count : _context.ItemCount; return IsVirtualizingContext ? _realizedElements.Count : _context!.ItemCount;
} }
public ILayoutable GetAt(int realizedIndex) public ILayoutable GetAt(int realizedIndex)
{ {
ILayoutable element; ILayoutable? element;
if (IsVirtualizingContext) if (IsVirtualizingContext)
{ {
if (_realizedElements[realizedIndex] == null) element = _realizedElements[realizedIndex];
if (element == null)
{ {
// Sentinel. Create the element now since we need it. // Sentinel. Create the element now since we need it.
int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex); int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex);
element = _context.GetOrCreateElementAt( element = _context!.GetOrCreateElementAt(
dataIndex, dataIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
_realizedElements[realizedIndex] = element; _realizedElements[realizedIndex] = element;
} }
else
{
element = _realizedElements[realizedIndex];
}
} }
else else
{ {
// realizedIndex and dataIndex are the same (everything is realized) // realizedIndex and dataIndex are the same (everything is realized)
element = _context.GetOrCreateElementAt( element = _context!.GetOrCreateElementAt(
realizedIndex, realizedIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
} }
@ -112,7 +110,7 @@ namespace Avalonia.Layout
_realizedElementLayoutBounds.Add(default); _realizedElementLayoutBounds.Add(default);
} }
public void Insert(int realizedIndex, int dataIndex, ILayoutable element) public void Insert(int realizedIndex, int dataIndex, ILayoutable? element)
{ {
if (realizedIndex == 0) if (realizedIndex == 0)
{ {
@ -136,7 +134,7 @@ namespace Avalonia.Layout
if (elementRef != null) if (elementRef != null)
{ {
_context.RecycleElement(elementRef); _context!.RecycleElement(elementRef);
} }
} }
@ -203,26 +201,26 @@ namespace Avalonia.Layout
else else
{ {
// Non virtualized - everything is realized // Non virtualized - everything is realized
return index >= 0 && index < _context.ItemCount; return index >= 0 && index < _context!.ItemCount;
} }
} }
public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context.ItemCount; public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context!.ItemCount;
public ILayoutable GetRealizedElement(int dataIndex) public ILayoutable? GetRealizedElement(int dataIndex)
{ {
return IsVirtualizingContext ? return IsVirtualizingContext ?
GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) : GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) :
_context.GetOrCreateElementAt( _context!.GetOrCreateElementAt(
dataIndex, dataIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
} }
public void EnsureElementRealized(bool forward, int dataIndex, string layoutId) public void EnsureElementRealized(bool forward, int dataIndex, string? layoutId)
{ {
if (IsDataIndexRealized(dataIndex) == false) if (IsDataIndexRealized(dataIndex) == false)
{ {
var element = _context.GetOrCreateElementAt( var element = _context!.GetOrCreateElementAt(
dataIndex, dataIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
@ -273,14 +271,14 @@ namespace Avalonia.Layout
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
{ {
OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); OnItemsAdded(args.NewStartingIndex, args.NewItems!.Count);
} }
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
{ {
int oldSize = args.OldItems.Count; int oldSize = args.OldItems!.Count;
int newSize = args.NewItems.Count; int newSize = args.NewItems!.Count;
int oldStartIndex = args.OldStartingIndex; int oldStartIndex = args.OldStartingIndex;
int newStartIndex = args.NewStartingIndex; int newStartIndex = args.NewStartingIndex;
@ -301,7 +299,7 @@ namespace Avalonia.Layout
if (elementRef != null) if (elementRef != null)
{ {
_context.RecycleElement(elementRef); _context!.RecycleElement(elementRef);
_realizedElements[realizedIndex] = null; _realizedElements[realizedIndex] = null;
} }
} }

68
src/Avalonia.Layout/FlowLayoutAlgorithm.cs

@ -16,8 +16,8 @@ namespace Avalonia.Layout
private Size _lastAvailableSize; private Size _lastAvailableSize;
private double _lastItemSpacing; private double _lastItemSpacing;
private bool _collectionChangePending; private bool _collectionChangePending;
private VirtualizingLayoutContext _context; private VirtualizingLayoutContext? _context;
private IFlowLayoutAlgorithmDelegates _algorithmCallbacks; private IFlowLayoutAlgorithmDelegates? _algorithmCallbacks;
private Rect _lastExtent; private Rect _lastExtent;
private int _firstRealizedDataIndexInsideRealizationWindow = -1; private int _firstRealizedDataIndexInsideRealizationWindow = -1;
private int _lastRealizedDataIndexInsideRealizationWindow = -1; private int _lastRealizedDataIndexInsideRealizationWindow = -1;
@ -46,7 +46,7 @@ namespace Avalonia.Layout
} }
} }
private Rect RealizationRect => IsVirtualizingContext ? _context.RealizationRect : new Rect(Size.Infinity); private Rect RealizationRect => IsVirtualizingContext ? _context!.RealizationRect : new Rect(Size.Infinity);
public void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks) public void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks)
{ {
@ -76,7 +76,7 @@ namespace Avalonia.Layout
int maxItemsPerLine, int maxItemsPerLine,
ScrollOrientation orientation, ScrollOrientation orientation,
bool disableVirtualization, bool disableVirtualization,
string layoutId) string? layoutId)
{ {
_orientation.ScrollOrientation = orientation; _orientation.ScrollOrientation = orientation;
@ -87,7 +87,7 @@ namespace Avalonia.Layout
layoutId, layoutId,
realizationRect); realizationRect);
var suggestedAnchorIndex = _context.RecommendedAnchorIndex; var suggestedAnchorIndex = _context!.RecommendedAnchorIndex;
if (_elementManager.IsIndexValidInData(suggestedAnchorIndex)) if (_elementManager.IsIndexValidInData(suggestedAnchorIndex))
{ {
var anchorRealized = _elementManager.IsDataIndexRealized(suggestedAnchorIndex); var anchorRealized = _elementManager.IsDataIndexRealized(suggestedAnchorIndex);
@ -124,7 +124,7 @@ namespace Avalonia.Layout
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
bool isWrapping, bool isWrapping,
LineAlignment lineAlignment, LineAlignment lineAlignment,
string layoutId) string? layoutId)
{ {
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId);
ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId); ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId);
@ -149,7 +149,7 @@ namespace Avalonia.Layout
Size availableSize, Size availableSize,
VirtualizingLayoutContext context) VirtualizingLayoutContext context)
{ {
var measureSize = _algorithmCallbacks.Algorithm_GetMeasureSize(index, availableSize, context); var measureSize = _algorithmCallbacks!.Algorithm_GetMeasureSize(index, availableSize, context);
element.Measure(measureSize); element.Measure(measureSize);
var provisionalArrangeSize = _algorithmCallbacks.Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize, context); var provisionalArrangeSize = _algorithmCallbacks.Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize, context);
_algorithmCallbacks.Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize, provisionalArrangeSize, context); _algorithmCallbacks.Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize, provisionalArrangeSize, context);
@ -161,7 +161,7 @@ namespace Avalonia.Layout
Size availableSize, Size availableSize,
bool isWrapping, bool isWrapping,
double minItemSpacing, double minItemSpacing,
string layoutId) string? layoutId)
{ {
int anchorIndex = -1; int anchorIndex = -1;
var anchorPosition= new Point(); var anchorPosition= new Point();
@ -170,7 +170,7 @@ namespace Avalonia.Layout
if (!IsVirtualizingContext) if (!IsVirtualizingContext)
{ {
// Non virtualizing host, start generating from the element 0 // Non virtualizing host, start generating from the element 0
anchorIndex = context.ItemCount > 0 ? 0 : -1; anchorIndex = context!.ItemCount > 0 ? 0 : -1;
} }
else else
{ {
@ -183,7 +183,7 @@ namespace Avalonia.Layout
_lastItemSpacing != minItemSpacing || _lastItemSpacing != minItemSpacing ||
_collectionChangePending); _collectionChangePending);
var suggestedAnchorIndex = _context.RecommendedAnchorIndex; var suggestedAnchorIndex = _context!.RecommendedAnchorIndex;
var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 && var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 &&
_elementManager.IsDataIndexRealized(suggestedAnchorIndex); _elementManager.IsDataIndexRealized(suggestedAnchorIndex);
@ -191,10 +191,10 @@ namespace Avalonia.Layout
if (isAnchorSuggestionValid) if (isAnchorSuggestionValid)
{ {
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex);
anchorIndex = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement( anchorIndex = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement(
suggestedAnchorIndex, suggestedAnchorIndex,
availableSize, availableSize,
context).Index; context!).Index;
if (_elementManager.IsDataIndexRealized(anchorIndex)) if (_elementManager.IsDataIndexRealized(anchorIndex))
{ {
@ -235,7 +235,7 @@ namespace Avalonia.Layout
// The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window // The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window
// but not the visible window. In that situation, we still need to produce a valid anchor. // but not the visible window. In that situation, we still need to produce a valid anchor.
var anchorInfo = _algorithmCallbacks.Algorithm_GetAnchorForRealizationRect(availableSize, context); var anchorInfo = _algorithmCallbacks!.Algorithm_GetAnchorForRealizationRect(availableSize, context!);
anchorIndex = anchorInfo.Index; anchorIndex = anchorInfo.Index;
anchorPosition = _orientation.MinorMajorPoint(0, anchorInfo.Offset); anchorPosition = _orientation.MinorMajorPoint(0, anchorInfo.Offset);
} }
@ -259,12 +259,12 @@ namespace Avalonia.Layout
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Disconnected Window - throwing away all realized elements", layoutId); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Disconnected Window - throwing away all realized elements", layoutId);
_elementManager.ClearRealizedRange(); _elementManager.ClearRealizedRange();
var anchor = _context.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); var anchor = _context!.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
_elementManager.Add(anchor, anchorIndex); _elementManager.Add(anchor, anchorIndex);
} }
var anchorElement = _elementManager.GetRealizedElement(anchorIndex); var anchorElement = _elementManager.GetRealizedElement(anchorIndex);
var desiredSize = MeasureElement(anchorElement, anchorIndex, availableSize, _context); var desiredSize = MeasureElement(anchorElement!, anchorIndex, availableSize, _context!);
var layoutBounds = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height); var layoutBounds = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height);
_elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds); _elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds);
@ -296,7 +296,7 @@ namespace Avalonia.Layout
double lineSpacing, double lineSpacing,
int maxItemsPerLine, int maxItemsPerLine,
bool disableVirtualization, bool disableVirtualization,
string layoutId) string? layoutId)
{ {
if (anchorIndex != -1) if (anchorIndex != -1)
{ {
@ -322,7 +322,7 @@ namespace Avalonia.Layout
// Ensure layout element. // Ensure layout element.
_elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId); _elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId);
var currentElement = _elementManager.GetRealizedElement(currentIndex); var currentElement = _elementManager.GetRealizedElement(currentIndex);
var desiredSize = MeasureElement(currentElement, currentIndex, availableSize, _context); var desiredSize = MeasureElement(currentElement!, currentIndex, availableSize, _context!);
++count; ++count;
// Lay it out. // Lay it out.
@ -333,7 +333,7 @@ namespace Avalonia.Layout
if (direction == GenerateDirection.Forward) if (direction == GenerateDirection.Forward)
{ {
double remainingSpace = _orientation.Minor(availableSize) - (_orientation.MinorStart(previousElementBounds) + _orientation.MinorSize(previousElementBounds) + minItemSpacing + _orientation.Minor(desiredSize)); double remainingSpace = _orientation.Minor(availableSize) - (_orientation.MinorStart(previousElementBounds) + _orientation.MinorSize(previousElementBounds) + minItemSpacing + _orientation.Minor(desiredSize));
if (countInLine >= maxItemsPerLine || _algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
{ {
// No more space in this row. wrap to next row. // No more space in this row. wrap to next row.
_orientation.SetMinorStart(ref currentBounds, 0); _orientation.SetMinorStart(ref currentBounds, 0);
@ -371,7 +371,7 @@ namespace Avalonia.Layout
{ {
// Backward // Backward
double remainingSpace = _orientation.MinorStart(previousElementBounds) - (_orientation.Minor(desiredSize) + minItemSpacing); double remainingSpace = _orientation.MinorStart(previousElementBounds) - (_orientation.Minor(desiredSize) + minItemSpacing);
if (countInLine >= maxItemsPerLine || _algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
{ {
// Does not fit, wrap to the previous row // Does not fit, wrap to the previous row
var availableSizeMinor = _orientation.Minor(availableSize); var availableSizeMinor = _orientation.Minor(availableSize);
@ -432,13 +432,13 @@ namespace Avalonia.Layout
// account for that element in the indices inside the realization window. // account for that element in the indices inside the realization window.
if (direction == GenerateDirection.Forward) if (direction == GenerateDirection.Forward)
{ {
int dataCount = _context.ItemCount; int dataCount = _context!.ItemCount;
_lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1; _lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1;
_lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow); _lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow);
} }
else else
{ {
int dataCount = _context.ItemCount; int dataCount = _context!.ItemCount;
_firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1; _firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1;
_firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow); _firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow);
} }
@ -454,7 +454,7 @@ namespace Avalonia.Layout
{ {
_elementManager.ClearRealizedRange(); _elementManager.ClearRealizedRange();
// FlowLayout requires that the anchor is the first element in the row. // FlowLayout requires that the anchor is the first element in the row.
var internalAnchor = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement(index, availableSize, context); var internalAnchor = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement(index, availableSize, context);
// No need to set the position of the anchor. // No need to set the position of the anchor.
// (0,0) is fine for now since the extent can // (0,0) is fine for now since the extent can
@ -487,7 +487,7 @@ namespace Avalonia.Layout
} }
else else
{ {
var realizationRect = _context.RealizationRect; var realizationRect = _context!.RealizationRect;
var elementBounds = _elementManager.GetLayoutBoundsForDataIndex(index); var elementBounds = _elementManager.GetLayoutBoundsForDataIndex(index);
var elementMajorStart = _orientation.MajorStart(elementBounds); var elementMajorStart = _orientation.MajorStart(elementBounds);
@ -510,11 +510,11 @@ namespace Avalonia.Layout
return shouldContinue; return shouldContinue;
} }
private Rect EstimateExtent(Size availableSize, string layoutId) private Rect EstimateExtent(Size availableSize, string? layoutId)
{ {
ILayoutable firstRealizedElement = null; ILayoutable? firstRealizedElement = null;
Rect firstBounds = new Rect(); Rect firstBounds = new Rect();
ILayoutable lastRealizedElement = null; ILayoutable? lastRealizedElement = null;
Rect lastBounds = new Rect(); Rect lastBounds = new Rect();
int firstDataIndex = -1; int firstDataIndex = -1;
int lastDataIndex = -1; int lastDataIndex = -1;
@ -531,9 +531,9 @@ namespace Avalonia.Layout
lastBounds = _elementManager.GetLayoutBoundsForRealizedIndex(last); lastBounds = _elementManager.GetLayoutBoundsForRealizedIndex(last);
} }
Rect extent = _algorithmCallbacks.Algorithm_GetExtent( Rect extent = _algorithmCallbacks!.Algorithm_GetExtent(
availableSize, availableSize,
_context, _context!,
firstRealizedElement, firstRealizedElement,
firstDataIndex, firstDataIndex,
firstBounds, firstBounds,
@ -563,7 +563,7 @@ namespace Avalonia.Layout
if (_orientation.MajorStart(currentBounds) != currentLineOffset) if (_orientation.MajorStart(currentBounds) != currentLineOffset)
{ {
// Staring a new line // Staring a new line
_algorithmCallbacks.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context); _algorithmCallbacks!.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context!);
countInLine = 0; countInLine = 0;
currentLineOffset = _orientation.MajorStart(currentBounds); currentLineOffset = _orientation.MajorStart(currentBounds);
currentLineSize = 0; currentLineSize = 0;
@ -575,7 +575,7 @@ namespace Avalonia.Layout
} }
// Raise for the last line. // Raise for the last line.
_algorithmCallbacks.Algorithm_OnLineArranged(_lastRealizedDataIndexInsideRealizationWindow - countInLine + 1, countInLine, currentLineSize, _context); _algorithmCallbacks!.Algorithm_OnLineArranged(_lastRealizedDataIndexInsideRealizationWindow - countInLine + 1, countInLine, currentLineSize, _context!);
} }
} }
} }
@ -584,7 +584,7 @@ namespace Avalonia.Layout
Size finalSize, Size finalSize,
LineAlignment lineAlignment, LineAlignment lineAlignment,
bool isWrapping, bool isWrapping,
string layoutId) string? layoutId)
{ {
// Walk through the realized elements one line at a time and // Walk through the realized elements one line at a time and
// align them, Then call element.Arrange with the arranged bounds. // align them, Then call element.Arrange with the arranged bounds.
@ -636,7 +636,7 @@ namespace Avalonia.Layout
LineAlignment lineAlignment, LineAlignment lineAlignment,
bool isWrapping, bool isWrapping,
Size finalSize, Size finalSize,
string layoutId) string? layoutId)
{ {
for (int rangeIndex = lineStartIndex; rangeIndex < lineStartIndex + countInLine; ++rangeIndex) for (int rangeIndex = lineStartIndex; rangeIndex < lineStartIndex + countInLine; ++rangeIndex)
{ {
@ -723,11 +723,11 @@ namespace Avalonia.Layout
{ {
if (IsVirtualizingContext) if (IsVirtualizingContext)
{ {
_context.LayoutOrigin = new Point(_lastExtent.X, _lastExtent.Y); _context!.LayoutOrigin = new Point(_lastExtent.X, _lastExtent.Y);
} }
} }
public ILayoutable GetElementIfRealized(int dataIndex) public ILayoutable? GetElementIfRealized(int dataIndex)
{ {
if (_elementManager.IsDataIndexRealized(dataIndex)) if (_elementManager.IsDataIndexRealized(dataIndex))
{ {

4
src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs

@ -21,10 +21,10 @@ namespace Avalonia.Layout
Rect Algorithm_GetExtent( Rect Algorithm_GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds); Rect lastRealizedLayoutBounds);
void Algorithm_OnElementMeasured( void Algorithm_OnElementMeasured(

4
src/Avalonia.Layout/LayoutContext.cs

@ -14,7 +14,7 @@ namespace Avalonia.Layout
/// <summary> /// <summary>
/// Gets or sets an object that represents the state of a layout. /// Gets or sets an object that represents the state of a layout.
/// </summary> /// </summary>
public object LayoutState public object? LayoutState
{ {
get => LayoutStateCore; get => LayoutStateCore;
set => LayoutStateCore = value; set => LayoutStateCore = value;
@ -23,6 +23,6 @@ namespace Avalonia.Layout
/// <summary> /// <summary>
/// Implements the behavior of <see cref="LayoutState"/> in a derived or custom LayoutContext. /// Implements the behavior of <see cref="LayoutState"/> in a derived or custom LayoutContext.
/// </summary> /// </summary>
protected virtual object LayoutStateCore { get; set; } protected virtual object? LayoutStateCore { get; set; }
} }
} }

2
src/Avalonia.Layout/LayoutContextAdapter.cs

@ -16,7 +16,7 @@ namespace Avalonia.Layout
_nonVirtualizingContext = nonVirtualizingContext; _nonVirtualizingContext = nonVirtualizingContext;
} }
protected override object LayoutStateCore protected override object? LayoutStateCore
{ {
get => _nonVirtualizingContext.LayoutState; get => _nonVirtualizingContext.LayoutState;
set => _nonVirtualizingContext.LayoutState = value; set => _nonVirtualizingContext.LayoutState = value;

2
src/Avalonia.Layout/LayoutHelper.cs

@ -101,7 +101,7 @@ namespace Avalonia.Layout
if (result == 0 || double.IsNaN(result) || double.IsInfinity(result)) if (result == 0 || double.IsNaN(result) || double.IsInfinity(result))
{ {
throw new Exception($"Invalid LayoutScaling returned from {visualRoot.GetType()}"); throw new Exception($"Invalid LayoutScaling returned from {visualRoot!.GetType()}");
} }
return result; return result;

1
src/Avalonia.Layout/LayoutQueue.cs

@ -5,6 +5,7 @@ using System.Collections.Generic;
namespace Avalonia.Layout namespace Avalonia.Layout
{ {
internal class LayoutQueue<T> : IReadOnlyCollection<T>, IDisposable internal class LayoutQueue<T> : IReadOnlyCollection<T>, IDisposable
where T : notnull
{ {
private struct Info private struct Info
{ {

2
src/Avalonia.Layout/Layoutable.cs

@ -794,7 +794,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
/// <param name="sender">The sender.</param> /// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void LayoutManagedLayoutUpdated(object sender, EventArgs e) => _layoutUpdated?.Invoke(this, e); private void LayoutManagedLayoutUpdated(object? sender, EventArgs e) => _layoutUpdated?.Invoke(this, e);
/// <summary> /// <summary>
/// Tests whether any of a <see cref="Rect"/>'s properties include negative values, /// Tests whether any of a <see cref="Rect"/>'s properties include negative values,

4
src/Avalonia.Layout/NonVirtualizingLayoutContext.cs

@ -12,7 +12,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public abstract class NonVirtualizingLayoutContext : LayoutContext public abstract class NonVirtualizingLayoutContext : LayoutContext
{ {
private VirtualizingLayoutContext _contextAdapter; private VirtualizingLayoutContext? _contextAdapter;
/// <summary> /// <summary>
/// Gets the collection of child controls from the container that provides the context. /// Gets the collection of child controls from the container that provides the context.
@ -26,6 +26,6 @@ namespace Avalonia.Layout
protected abstract IReadOnlyList<ILayoutable> ChildrenCore { get; } protected abstract IReadOnlyList<ILayoutable> ChildrenCore { get; }
internal VirtualizingLayoutContext GetVirtualizingContextAdapter() => internal VirtualizingLayoutContext GetVirtualizingContextAdapter() =>
_contextAdapter ?? (_contextAdapter = new LayoutContextAdapter(this)); _contextAdapter ??= new LayoutContextAdapter(this);
} }
} }

22
src/Avalonia.Layout/StackLayout.cs

@ -78,10 +78,10 @@ namespace Avalonia.Layout
internal Rect GetExtent( internal Rect GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds) Rect lastRealizedLayoutBounds)
{ {
@ -89,7 +89,7 @@ namespace Avalonia.Layout
// Constants // Constants
int itemsCount = context.ItemCount; int itemsCount = context.ItemCount;
var stackState = (StackLayoutState)context.LayoutState; var stackState = (StackLayoutState)context.LayoutState!;
double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing; double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing;
_orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds); _orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds);
@ -131,7 +131,7 @@ namespace Avalonia.Layout
{ {
if (context is VirtualizingLayoutContext virtualContext) if (context is VirtualizingLayoutContext virtualContext)
{ {
var stackState = (StackLayoutState)virtualContext.LayoutState; var stackState = (StackLayoutState)virtualContext.LayoutState!;
var provisionalArrangeSizeWinRt = provisionalArrangeSize; var provisionalArrangeSizeWinRt = provisionalArrangeSize;
stackState.OnElementMeasured( stackState.OnElementMeasured(
index, index,
@ -177,7 +177,7 @@ namespace Avalonia.Layout
if (targetIndex >= 0 && targetIndex < itemsCount) if (targetIndex >= 0 && targetIndex < itemsCount)
{ {
index = targetIndex; index = targetIndex;
var state = (StackLayoutState)context.LayoutState; var state = (StackLayoutState)context.LayoutState!;
double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing;
offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent); offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent);
} }
@ -188,10 +188,10 @@ namespace Avalonia.Layout
Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds) Rect lastRealizedLayoutBounds)
{ {
@ -234,7 +234,7 @@ namespace Avalonia.Layout
if (itemsCount > 0) if (itemsCount > 0)
{ {
var realizationRect = context.RealizationRect; var realizationRect = context.RealizationRect;
var state = (StackLayoutState)context.LayoutState; var state = (StackLayoutState)context.LayoutState!;
var lastExtent = state.FlowAlgorithm.LastExtent; var lastExtent = state.FlowAlgorithm.LastExtent;
double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing;
@ -279,13 +279,13 @@ namespace Avalonia.Layout
protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context)
{ {
var stackState = (StackLayoutState)context.LayoutState; var stackState = (StackLayoutState)context.LayoutState!;
stackState.UninitializeForContext(context); stackState.UninitializeForContext(context);
} }
protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{ {
((StackLayoutState)context.LayoutState).OnMeasureStart(); ((StackLayoutState)context.LayoutState!).OnMeasureStart();
var desiredSize = GetFlowAlgorithm(context).Measure( var desiredSize = GetFlowAlgorithm(context).Measure(
availableSize, availableSize,
@ -358,6 +358,6 @@ namespace Avalonia.Layout
private void InvalidateLayout() => InvalidateMeasure(); private void InvalidateLayout() => InvalidateMeasure();
private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState).FlowAlgorithm; private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState!).FlowAlgorithm;
} }
} }

26
src/Avalonia.Layout/UniformGridLayout.cs

@ -258,7 +258,7 @@ namespace Avalonia.Layout
Size availableSize, Size availableSize,
VirtualizingLayoutContext context) VirtualizingLayoutContext context)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight);
} }
@ -268,7 +268,7 @@ namespace Avalonia.Layout
Size desiredSize, Size desiredSize,
VirtualizingLayoutContext context) VirtualizingLayoutContext context)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight);
} }
@ -285,7 +285,7 @@ namespace Avalonia.Layout
var realizationRect = context.RealizationRect; var realizationRect = context.RealizationRect;
if (itemsCount > 0 && _orientation.MajorSize(realizationRect) > 0) if (itemsCount > 0 && _orientation.MajorSize(realizationRect) > 0)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
var lastExtent = gridState.FlowAlgorithm.LastExtent; var lastExtent = gridState.FlowAlgorithm.LastExtent;
var itemsPerLine = Math.Min( // note use of unsigned ints var itemsPerLine = Math.Min( // note use of unsigned ints
Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))), Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
@ -324,7 +324,7 @@ namespace Avalonia.Layout
Math.Max(1u, _maximumRowsOrColumns)); Math.Max(1u, _maximumRowsOrColumns));
int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine; int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine;
index = indexOfFirstInLine; index = indexOfFirstInLine;
var state = context.LayoutState as UniformGridLayoutState; var state = (UniformGridLayoutState)context.LayoutState!;
offset = _orientation.MajorStart(GetLayoutRectForDataIndex(availableSize, indexOfFirstInLine, state.FlowAlgorithm.LastExtent, context)); offset = _orientation.MajorStart(GetLayoutRectForDataIndex(availableSize, indexOfFirstInLine, state.FlowAlgorithm.LastExtent, context));
} }
@ -338,10 +338,10 @@ namespace Avalonia.Layout
Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds) Rect lastRealizedLayoutBounds)
{ {
@ -421,7 +421,7 @@ namespace Avalonia.Layout
protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
gridState.UninitializeForContext(context); gridState.UninitializeForContext(context);
} }
@ -429,7 +429,7 @@ namespace Avalonia.Layout
{ {
// Set the width and height on the grid state. If the user already set them then use the preset. // Set the width and height on the grid state. If the user already set them then use the preset.
// If not, we have to measure the first element and get back a size which we're going to be using for the rest of the items. // If not, we have to measure the first element and get back a size which we're going to be using for the rest of the items.
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns); gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns);
var desiredSize = GetFlowAlgorithm(context).Measure( var desiredSize = GetFlowAlgorithm(context).Measure(
@ -467,7 +467,7 @@ namespace Avalonia.Layout
// Always invalidate layout to keep the view accurate. // Always invalidate layout to keep the view accurate.
InvalidateLayout(); InvalidateLayout();
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
gridState.ClearElementOnDataSourceChange(context, args); gridState.ClearElementOnDataSourceChange(context, args);
} }
@ -517,7 +517,7 @@ namespace Avalonia.Layout
private double GetMinorSizeWithSpacing(VirtualizingLayoutContext context) private double GetMinorSizeWithSpacing(VirtualizingLayoutContext context)
{ {
var minItemSpacing = MinItemSpacing; var minItemSpacing = MinItemSpacing;
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return _orientation.ScrollOrientation == ScrollOrientation.Vertical? return _orientation.ScrollOrientation == ScrollOrientation.Vertical?
gridState.EffectiveItemWidth + minItemSpacing : gridState.EffectiveItemWidth + minItemSpacing :
gridState.EffectiveItemHeight + minItemSpacing; gridState.EffectiveItemHeight + minItemSpacing;
@ -526,7 +526,7 @@ namespace Avalonia.Layout
private double GetMajorSizeWithSpacing(VirtualizingLayoutContext context) private double GetMajorSizeWithSpacing(VirtualizingLayoutContext context)
{ {
var lineSpacing = LineSpacing; var lineSpacing = LineSpacing;
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return _orientation.ScrollOrientation == ScrollOrientation.Vertical ? return _orientation.ScrollOrientation == ScrollOrientation.Vertical ?
gridState.EffectiveItemHeight + lineSpacing : gridState.EffectiveItemHeight + lineSpacing :
gridState.EffectiveItemWidth + lineSpacing; gridState.EffectiveItemWidth + lineSpacing;
@ -544,7 +544,7 @@ namespace Avalonia.Layout
int rowIndex = (int)(index / itemsPerLine); int rowIndex = (int)(index / itemsPerLine);
int indexInRow = index - (rowIndex * itemsPerLine); int indexInRow = index - (rowIndex * itemsPerLine);
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
Rect bounds = _orientation.MinorMajorRect( Rect bounds = _orientation.MinorMajorRect(
indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent), indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent),
rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent), rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent),
@ -556,6 +556,6 @@ namespace Avalonia.Layout
private void InvalidateLayout() => InvalidateMeasure(); private void InvalidateLayout() => InvalidateMeasure();
private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState).FlowAlgorithm; private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState!).FlowAlgorithm;
} }
} }

2
src/Avalonia.Layout/UniformGridLayoutState.cs

@ -18,7 +18,7 @@ namespace Avalonia.Layout
// If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it. // If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it.
// If we are responsible for clearing element 0 we keep m_cachedFirstElement valid. // If we are responsible for clearing element 0 we keep m_cachedFirstElement valid.
// If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm. // If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm.
private ILayoutable _cachedFirstElement; private ILayoutable? _cachedFirstElement;
internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm(); internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm();
internal double EffectiveItemWidth { get; private set; } internal double EffectiveItemWidth { get; private set; }

5
src/Avalonia.Layout/Utils/ListUtils.cs

@ -23,10 +23,5 @@ namespace Avalonia.Layout.Utils
list.AddRange(Enumerable.Repeat(value, size - cur)); list.AddRange(Enumerable.Repeat(value, size - cur));
} }
} }
public static void Resize<T>(this List<T> list, int count)
{
Resize(list, count, default);
}
} }
} }

4
src/Avalonia.Layout/VirtualLayoutContextAdapter.cs

@ -6,14 +6,14 @@ namespace Avalonia.Layout
public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext
{ {
private readonly VirtualizingLayoutContext _virtualizingContext; private readonly VirtualizingLayoutContext _virtualizingContext;
private ChildrenCollection _children; private ChildrenCollection? _children;
public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext) public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext)
{ {
_virtualizingContext = virtualizingContext; _virtualizingContext = virtualizingContext;
} }
protected override object LayoutStateCore protected override object? LayoutStateCore
{ {
get => _virtualizingContext.LayoutState; get => _virtualizingContext.LayoutState;
set => _virtualizingContext.LayoutState = value; set => _virtualizingContext.LayoutState = value;

2
src/Avalonia.Layout/VirtualizingLayoutContext.cs

@ -43,7 +43,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public abstract class VirtualizingLayoutContext : LayoutContext public abstract class VirtualizingLayoutContext : LayoutContext
{ {
private NonVirtualizingLayoutContext _contextAdapter; private NonVirtualizingLayoutContext? _contextAdapter;
/// <summary> /// <summary>
/// Gets the number of items in the data. /// Gets the number of items in the data.

2
src/Avalonia.Layout/WrapLayout/UvMeasure.cs

@ -27,7 +27,7 @@ namespace Avalonia.Layout
} }
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (obj is UvMeasure measure) if (obj is UvMeasure measure)
{ {

2
src/Avalonia.Layout/WrapLayout/WrapItem.cs

@ -20,6 +20,6 @@ namespace Avalonia.Layout
public UvMeasure? Position { get; internal set; } public UvMeasure? Position { get; internal set; }
public ILayoutable Element { get; internal set; } public ILayoutable? Element { get; internal set; }
} }
} }

6
src/Avalonia.Layout/WrapLayout/WrapLayout.cs

@ -88,7 +88,7 @@ namespace Avalonia.Layout
/// <inheritdoc /> /// <inheritdoc />
protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args) protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args)
{ {
var state = (WrapLayoutState)context.LayoutState; var state = (WrapLayoutState)context.LayoutState!;
switch (args.Action) switch (args.Action)
{ {
@ -126,7 +126,7 @@ namespace Avalonia.Layout
var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var realizationBounds = new UvBounds(Orientation, context.RealizationRect);
var position = UvMeasure.Zero; var position = UvMeasure.Zero;
var state = (WrapLayoutState)context.LayoutState; var state = (WrapLayoutState)context.LayoutState!;
if (state.Orientation != Orientation) if (state.Orientation != Orientation)
{ {
state.SetOrientation(Orientation); state.SetOrientation(Orientation);
@ -261,7 +261,7 @@ namespace Avalonia.Layout
var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var realizationBounds = new UvBounds(Orientation, context.RealizationRect);
var state = (WrapLayoutState)context.LayoutState; var state = (WrapLayoutState)context.LayoutState!;
bool Arrange(WrapItem item, bool isLast = false) bool Arrange(WrapItem item, bool isLast = false)
{ {
if (item.Measure.HasValue == false) if (item.Measure.HasValue == false)

9
src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs

@ -62,8 +62,11 @@ namespace Avalonia.Layout
internal void SetOrientation(Orientation orientation) internal void SetOrientation(Orientation orientation)
{ {
foreach (var item in _items.Where(i => i.Measure.HasValue)) foreach (var item in _items)
{ {
if (!item.Measure.HasValue)
continue;
UvMeasure measure = item.Measure.Value; UvMeasure measure = item.Measure.Value;
double v = measure.V; double v = measure.V;
measure.V = measure.U; measure.V = measure.U;
@ -120,10 +123,10 @@ namespace Avalonia.Layout
} }
lastPosition = item.Position; lastPosition = item.Position;
maxV = Math.Max(maxV, item.Measure.Value.V); maxV = Math.Max(maxV, item.Measure!.Value.V);
} }
double totalHeight = lastPosition.Value.V + maxV; double totalHeight = lastPosition!.Value.V + maxV;
if (calculateAverage) if (calculateAverage)
{ {
return (totalHeight / itemCount) * _context.ItemCount; return (totalHeight / itemCount) * _context.ItemCount;

Loading…
Cancel
Save