From f8b399257e7ee6259b50b64c242f64d928ecd244 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 20 Dec 2021 09:46:32 +0100 Subject: [PATCH] Merge pull request #7210 from AvaloniaUI/fixes/avalonia-layout-nullability Added nullable annotations to Avalonia.Layout. --- src/Avalonia.Layout/AttachedLayout.cs | 6 +- src/Avalonia.Layout/Avalonia.Layout.csproj | 1 + src/Avalonia.Layout/ElementManager.cs | 46 ++++++------- src/Avalonia.Layout/FlowLayoutAlgorithm.cs | 68 +++++++++---------- .../IFlowLayoutAlgorithmDelegates.cs | 4 +- src/Avalonia.Layout/LayoutContext.cs | 4 +- src/Avalonia.Layout/LayoutContextAdapter.cs | 2 +- src/Avalonia.Layout/LayoutHelper.cs | 2 +- src/Avalonia.Layout/LayoutQueue.cs | 1 + src/Avalonia.Layout/Layoutable.cs | 2 +- .../NonVirtualizingLayoutContext.cs | 4 +- src/Avalonia.Layout/StackLayout.cs | 22 +++--- src/Avalonia.Layout/UniformGridLayout.cs | 26 +++---- src/Avalonia.Layout/UniformGridLayoutState.cs | 2 +- src/Avalonia.Layout/Utils/ListUtils.cs | 5 -- .../VirtualLayoutContextAdapter.cs | 4 +- .../VirtualizingLayoutContext.cs | 2 +- src/Avalonia.Layout/WrapLayout/UvMeasure.cs | 2 +- src/Avalonia.Layout/WrapLayout/WrapItem.cs | 2 +- src/Avalonia.Layout/WrapLayout/WrapLayout.cs | 6 +- .../WrapLayout/WrapLayoutState.cs | 9 ++- 21 files changed, 109 insertions(+), 111 deletions(-) diff --git a/src/Avalonia.Layout/AttachedLayout.cs b/src/Avalonia.Layout/AttachedLayout.cs index 047c01343f..6c884641f8 100644 --- a/src/Avalonia.Layout/AttachedLayout.cs +++ b/src/Avalonia.Layout/AttachedLayout.cs @@ -12,17 +12,17 @@ namespace Avalonia.Layout /// public abstract class AttachedLayout : AvaloniaObject { - public string LayoutId { get; set; } + public string? LayoutId { get; set; } /// /// Occurs when the measurement state (layout) has been invalidated. /// - public event EventHandler MeasureInvalidated; + public event EventHandler? MeasureInvalidated; /// /// Occurs when the arrange state (layout) has been invalidated. /// - public event EventHandler ArrangeInvalidated; + public event EventHandler? ArrangeInvalidated; /// /// Initializes any per-container state the layout requires when it is attached to an diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj index 076ec56cd6..47c41d358d 100644 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ b/src/Avalonia.Layout/Avalonia.Layout.csproj @@ -9,4 +9,5 @@ + diff --git a/src/Avalonia.Layout/ElementManager.cs b/src/Avalonia.Layout/ElementManager.cs index 681d23a61f..9a033ca447 100644 --- a/src/Avalonia.Layout/ElementManager.cs +++ b/src/Avalonia.Layout/ElementManager.cs @@ -13,10 +13,10 @@ namespace Avalonia.Layout { internal class ElementManager { - private readonly List _realizedElements = new List(); + private readonly List _realizedElements = new List(); private readonly List _realizedElementLayoutBounds = new List(); private int _firstRealizedDataIndex; - private VirtualizingLayoutContext _context; + private VirtualizingLayoutContext? _context; private bool IsVirtualizingContext { @@ -58,7 +58,7 @@ namespace Avalonia.Layout // Make sure there is enough space for the bounds. // Note: We could optimize when the count becomes smaller, but keeping // 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() { - return IsVirtualizingContext ? _realizedElements.Count : _context.ItemCount; + return IsVirtualizingContext ? _realizedElements.Count : _context!.ItemCount; } public ILayoutable GetAt(int realizedIndex) { - ILayoutable element; + ILayoutable? element; if (IsVirtualizingContext) { - if (_realizedElements[realizedIndex] == null) + element = _realizedElements[realizedIndex]; + + if (element == null) { // Sentinel. Create the element now since we need it. int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex); - element = _context.GetOrCreateElementAt( + element = _context!.GetOrCreateElementAt( dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); _realizedElements[realizedIndex] = element; } - else - { - element = _realizedElements[realizedIndex]; - } } else { // realizedIndex and dataIndex are the same (everything is realized) - element = _context.GetOrCreateElementAt( + element = _context!.GetOrCreateElementAt( realizedIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); } @@ -112,7 +110,7 @@ namespace Avalonia.Layout _realizedElementLayoutBounds.Add(default); } - public void Insert(int realizedIndex, int dataIndex, ILayoutable element) + public void Insert(int realizedIndex, int dataIndex, ILayoutable? element) { if (realizedIndex == 0) { @@ -136,7 +134,7 @@ namespace Avalonia.Layout if (elementRef != null) { - _context.RecycleElement(elementRef); + _context!.RecycleElement(elementRef); } } @@ -203,26 +201,26 @@ namespace Avalonia.Layout else { // 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 ? GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) : - _context.GetOrCreateElementAt( + _context!.GetOrCreateElementAt( dataIndex, 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) { - var element = _context.GetOrCreateElementAt( + var element = _context!.GetOrCreateElementAt( dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); @@ -273,14 +271,14 @@ namespace Avalonia.Layout { case NotifyCollectionChangedAction.Add: { - OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); + OnItemsAdded(args.NewStartingIndex, args.NewItems!.Count); } break; case NotifyCollectionChangedAction.Replace: { - int oldSize = args.OldItems.Count; - int newSize = args.NewItems.Count; + int oldSize = args.OldItems!.Count; + int newSize = args.NewItems!.Count; int oldStartIndex = args.OldStartingIndex; int newStartIndex = args.NewStartingIndex; @@ -301,7 +299,7 @@ namespace Avalonia.Layout if (elementRef != null) { - _context.RecycleElement(elementRef); + _context!.RecycleElement(elementRef); _realizedElements[realizedIndex] = null; } } diff --git a/src/Avalonia.Layout/FlowLayoutAlgorithm.cs b/src/Avalonia.Layout/FlowLayoutAlgorithm.cs index 63343fd1a7..40b392225f 100644 --- a/src/Avalonia.Layout/FlowLayoutAlgorithm.cs +++ b/src/Avalonia.Layout/FlowLayoutAlgorithm.cs @@ -16,8 +16,8 @@ namespace Avalonia.Layout private Size _lastAvailableSize; private double _lastItemSpacing; private bool _collectionChangePending; - private VirtualizingLayoutContext _context; - private IFlowLayoutAlgorithmDelegates _algorithmCallbacks; + private VirtualizingLayoutContext? _context; + private IFlowLayoutAlgorithmDelegates? _algorithmCallbacks; private Rect _lastExtent; private int _firstRealizedDataIndexInsideRealizationWindow = -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) { @@ -76,7 +76,7 @@ namespace Avalonia.Layout int maxItemsPerLine, ScrollOrientation orientation, bool disableVirtualization, - string layoutId) + string? layoutId) { _orientation.ScrollOrientation = orientation; @@ -87,7 +87,7 @@ namespace Avalonia.Layout layoutId, realizationRect); - var suggestedAnchorIndex = _context.RecommendedAnchorIndex; + var suggestedAnchorIndex = _context!.RecommendedAnchorIndex; if (_elementManager.IsIndexValidInData(suggestedAnchorIndex)) { var anchorRealized = _elementManager.IsDataIndexRealized(suggestedAnchorIndex); @@ -124,7 +124,7 @@ namespace Avalonia.Layout VirtualizingLayoutContext context, bool isWrapping, LineAlignment lineAlignment, - string layoutId) + string? layoutId) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId); ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId); @@ -149,7 +149,7 @@ namespace Avalonia.Layout Size availableSize, VirtualizingLayoutContext context) { - var measureSize = _algorithmCallbacks.Algorithm_GetMeasureSize(index, availableSize, context); + var measureSize = _algorithmCallbacks!.Algorithm_GetMeasureSize(index, availableSize, context); element.Measure(measureSize); var provisionalArrangeSize = _algorithmCallbacks.Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize, context); _algorithmCallbacks.Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize, provisionalArrangeSize, context); @@ -161,7 +161,7 @@ namespace Avalonia.Layout Size availableSize, bool isWrapping, double minItemSpacing, - string layoutId) + string? layoutId) { int anchorIndex = -1; var anchorPosition= new Point(); @@ -170,7 +170,7 @@ namespace Avalonia.Layout if (!IsVirtualizingContext) { // Non virtualizing host, start generating from the element 0 - anchorIndex = context.ItemCount > 0 ? 0 : -1; + anchorIndex = context!.ItemCount > 0 ? 0 : -1; } else { @@ -183,7 +183,7 @@ namespace Avalonia.Layout _lastItemSpacing != minItemSpacing || _collectionChangePending); - var suggestedAnchorIndex = _context.RecommendedAnchorIndex; + var suggestedAnchorIndex = _context!.RecommendedAnchorIndex; var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 && _elementManager.IsDataIndexRealized(suggestedAnchorIndex); @@ -191,10 +191,10 @@ namespace Avalonia.Layout if (isAnchorSuggestionValid) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex); - anchorIndex = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement( + anchorIndex = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement( suggestedAnchorIndex, availableSize, - context).Index; + context!).Index; 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 // 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; 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); _elementManager.ClearRealizedRange(); - var anchor = _context.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); + var anchor = _context!.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); _elementManager.Add(anchor, 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); _elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds); @@ -296,7 +296,7 @@ namespace Avalonia.Layout double lineSpacing, int maxItemsPerLine, bool disableVirtualization, - string layoutId) + string? layoutId) { if (anchorIndex != -1) { @@ -322,7 +322,7 @@ namespace Avalonia.Layout // Ensure layout element. _elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId); var currentElement = _elementManager.GetRealizedElement(currentIndex); - var desiredSize = MeasureElement(currentElement, currentIndex, availableSize, _context); + var desiredSize = MeasureElement(currentElement!, currentIndex, availableSize, _context!); ++count; // Lay it out. @@ -333,7 +333,7 @@ namespace Avalonia.Layout if (direction == GenerateDirection.Forward) { 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. _orientation.SetMinorStart(ref currentBounds, 0); @@ -371,7 +371,7 @@ namespace Avalonia.Layout { // Backward 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 var availableSizeMinor = _orientation.Minor(availableSize); @@ -432,13 +432,13 @@ namespace Avalonia.Layout // account for that element in the indices inside the realization window. if (direction == GenerateDirection.Forward) { - int dataCount = _context.ItemCount; + int dataCount = _context!.ItemCount; _lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1; _lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow); } else { - int dataCount = _context.ItemCount; + int dataCount = _context!.ItemCount; _firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1; _firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow); } @@ -454,7 +454,7 @@ namespace Avalonia.Layout { _elementManager.ClearRealizedRange(); // 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. // (0,0) is fine for now since the extent can @@ -487,7 +487,7 @@ namespace Avalonia.Layout } else { - var realizationRect = _context.RealizationRect; + var realizationRect = _context!.RealizationRect; var elementBounds = _elementManager.GetLayoutBoundsForDataIndex(index); var elementMajorStart = _orientation.MajorStart(elementBounds); @@ -510,11 +510,11 @@ namespace Avalonia.Layout 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(); - ILayoutable lastRealizedElement = null; + ILayoutable? lastRealizedElement = null; Rect lastBounds = new Rect(); int firstDataIndex = -1; int lastDataIndex = -1; @@ -531,9 +531,9 @@ namespace Avalonia.Layout lastBounds = _elementManager.GetLayoutBoundsForRealizedIndex(last); } - Rect extent = _algorithmCallbacks.Algorithm_GetExtent( + Rect extent = _algorithmCallbacks!.Algorithm_GetExtent( availableSize, - _context, + _context!, firstRealizedElement, firstDataIndex, firstBounds, @@ -563,7 +563,7 @@ namespace Avalonia.Layout if (_orientation.MajorStart(currentBounds) != currentLineOffset) { // Staring a new line - _algorithmCallbacks.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context); + _algorithmCallbacks!.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context!); countInLine = 0; currentLineOffset = _orientation.MajorStart(currentBounds); currentLineSize = 0; @@ -575,7 +575,7 @@ namespace Avalonia.Layout } // 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, LineAlignment lineAlignment, bool isWrapping, - string layoutId) + string? layoutId) { // Walk through the realized elements one line at a time and // align them, Then call element.Arrange with the arranged bounds. @@ -636,7 +636,7 @@ namespace Avalonia.Layout LineAlignment lineAlignment, bool isWrapping, Size finalSize, - string layoutId) + string? layoutId) { for (int rangeIndex = lineStartIndex; rangeIndex < lineStartIndex + countInLine; ++rangeIndex) { @@ -723,11 +723,11 @@ namespace Avalonia.Layout { 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)) { diff --git a/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs b/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs index 907a3adf0f..e3eae0caf7 100644 --- a/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs +++ b/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs @@ -21,10 +21,10 @@ namespace Avalonia.Layout Rect Algorithm_GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds); void Algorithm_OnElementMeasured( diff --git a/src/Avalonia.Layout/LayoutContext.cs b/src/Avalonia.Layout/LayoutContext.cs index dadce58c0c..90bfc0200c 100644 --- a/src/Avalonia.Layout/LayoutContext.cs +++ b/src/Avalonia.Layout/LayoutContext.cs @@ -14,7 +14,7 @@ namespace Avalonia.Layout /// /// Gets or sets an object that represents the state of a layout. /// - public object LayoutState + public object? LayoutState { get => LayoutStateCore; set => LayoutStateCore = value; @@ -23,6 +23,6 @@ namespace Avalonia.Layout /// /// Implements the behavior of in a derived or custom LayoutContext. /// - protected virtual object LayoutStateCore { get; set; } + protected virtual object? LayoutStateCore { get; set; } } } diff --git a/src/Avalonia.Layout/LayoutContextAdapter.cs b/src/Avalonia.Layout/LayoutContextAdapter.cs index 695866df94..25132c2802 100644 --- a/src/Avalonia.Layout/LayoutContextAdapter.cs +++ b/src/Avalonia.Layout/LayoutContextAdapter.cs @@ -16,7 +16,7 @@ namespace Avalonia.Layout _nonVirtualizingContext = nonVirtualizingContext; } - protected override object LayoutStateCore + protected override object? LayoutStateCore { get => _nonVirtualizingContext.LayoutState; set => _nonVirtualizingContext.LayoutState = value; diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index 2fe59b49ca..71a2fa085b 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -101,7 +101,7 @@ namespace Avalonia.Layout 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; diff --git a/src/Avalonia.Layout/LayoutQueue.cs b/src/Avalonia.Layout/LayoutQueue.cs index 1a9eb6b785..24adeb0793 100644 --- a/src/Avalonia.Layout/LayoutQueue.cs +++ b/src/Avalonia.Layout/LayoutQueue.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; namespace Avalonia.Layout { internal class LayoutQueue : IReadOnlyCollection, IDisposable + where T : notnull { private struct Info { diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 7568ea8e09..09e0c4263a 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -794,7 +794,7 @@ namespace Avalonia.Layout /// /// The sender. /// The event args. - private void LayoutManagedLayoutUpdated(object sender, EventArgs e) => _layoutUpdated?.Invoke(this, e); + private void LayoutManagedLayoutUpdated(object? sender, EventArgs e) => _layoutUpdated?.Invoke(this, e); /// /// Tests whether any of a 's properties include negative values, diff --git a/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs b/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs index cef551f32e..91348f5e6d 100644 --- a/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs +++ b/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs @@ -12,7 +12,7 @@ namespace Avalonia.Layout /// public abstract class NonVirtualizingLayoutContext : LayoutContext { - private VirtualizingLayoutContext _contextAdapter; + private VirtualizingLayoutContext? _contextAdapter; /// /// Gets the collection of child controls from the container that provides the context. @@ -26,6 +26,6 @@ namespace Avalonia.Layout protected abstract IReadOnlyList ChildrenCore { get; } internal VirtualizingLayoutContext GetVirtualizingContextAdapter() => - _contextAdapter ?? (_contextAdapter = new LayoutContextAdapter(this)); + _contextAdapter ??= new LayoutContextAdapter(this); } } diff --git a/src/Avalonia.Layout/StackLayout.cs b/src/Avalonia.Layout/StackLayout.cs index 4a93c8344f..32e9c4dc8e 100644 --- a/src/Avalonia.Layout/StackLayout.cs +++ b/src/Avalonia.Layout/StackLayout.cs @@ -78,10 +78,10 @@ namespace Avalonia.Layout internal Rect GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds) { @@ -89,7 +89,7 @@ namespace Avalonia.Layout // Constants int itemsCount = context.ItemCount; - var stackState = (StackLayoutState)context.LayoutState; + var stackState = (StackLayoutState)context.LayoutState!; double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing; _orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds); @@ -131,7 +131,7 @@ namespace Avalonia.Layout { if (context is VirtualizingLayoutContext virtualContext) { - var stackState = (StackLayoutState)virtualContext.LayoutState; + var stackState = (StackLayoutState)virtualContext.LayoutState!; var provisionalArrangeSizeWinRt = provisionalArrangeSize; stackState.OnElementMeasured( index, @@ -177,7 +177,7 @@ namespace Avalonia.Layout if (targetIndex >= 0 && targetIndex < itemsCount) { index = targetIndex; - var state = (StackLayoutState)context.LayoutState; + var state = (StackLayoutState)context.LayoutState!; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent); } @@ -188,10 +188,10 @@ namespace Avalonia.Layout Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds) { @@ -234,7 +234,7 @@ namespace Avalonia.Layout if (itemsCount > 0) { var realizationRect = context.RealizationRect; - var state = (StackLayoutState)context.LayoutState; + var state = (StackLayoutState)context.LayoutState!; var lastExtent = state.FlowAlgorithm.LastExtent; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; @@ -279,13 +279,13 @@ namespace Avalonia.Layout protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) { - var stackState = (StackLayoutState)context.LayoutState; + var stackState = (StackLayoutState)context.LayoutState!; stackState.UninitializeForContext(context); } protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { - ((StackLayoutState)context.LayoutState).OnMeasureStart(); + ((StackLayoutState)context.LayoutState!).OnMeasureStart(); var desiredSize = GetFlowAlgorithm(context).Measure( availableSize, @@ -358,6 +358,6 @@ namespace Avalonia.Layout private void InvalidateLayout() => InvalidateMeasure(); - private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState).FlowAlgorithm; + private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState!).FlowAlgorithm; } } diff --git a/src/Avalonia.Layout/UniformGridLayout.cs b/src/Avalonia.Layout/UniformGridLayout.cs index 6b147590ab..508113834a 100644 --- a/src/Avalonia.Layout/UniformGridLayout.cs +++ b/src/Avalonia.Layout/UniformGridLayout.cs @@ -258,7 +258,7 @@ namespace Avalonia.Layout Size availableSize, VirtualizingLayoutContext context) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); } @@ -268,7 +268,7 @@ namespace Avalonia.Layout Size desiredSize, VirtualizingLayoutContext context) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); } @@ -285,7 +285,7 @@ namespace Avalonia.Layout var realizationRect = context.RealizationRect; if (itemsCount > 0 && _orientation.MajorSize(realizationRect) > 0) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; var lastExtent = gridState.FlowAlgorithm.LastExtent; var itemsPerLine = Math.Min( // note use of unsigned ints Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))), @@ -324,7 +324,7 @@ namespace Avalonia.Layout Math.Max(1u, _maximumRowsOrColumns)); int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine; index = indexOfFirstInLine; - var state = context.LayoutState as UniformGridLayoutState; + var state = (UniformGridLayoutState)context.LayoutState!; offset = _orientation.MajorStart(GetLayoutRectForDataIndex(availableSize, indexOfFirstInLine, state.FlowAlgorithm.LastExtent, context)); } @@ -338,10 +338,10 @@ namespace Avalonia.Layout Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds) { @@ -421,7 +421,7 @@ namespace Avalonia.Layout protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; 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. // 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); var desiredSize = GetFlowAlgorithm(context).Measure( @@ -467,7 +467,7 @@ namespace Avalonia.Layout // Always invalidate layout to keep the view accurate. InvalidateLayout(); - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; gridState.ClearElementOnDataSourceChange(context, args); } @@ -517,7 +517,7 @@ namespace Avalonia.Layout private double GetMinorSizeWithSpacing(VirtualizingLayoutContext context) { var minItemSpacing = MinItemSpacing; - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return _orientation.ScrollOrientation == ScrollOrientation.Vertical? gridState.EffectiveItemWidth + minItemSpacing : gridState.EffectiveItemHeight + minItemSpacing; @@ -526,7 +526,7 @@ namespace Avalonia.Layout private double GetMajorSizeWithSpacing(VirtualizingLayoutContext context) { var lineSpacing = LineSpacing; - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return _orientation.ScrollOrientation == ScrollOrientation.Vertical ? gridState.EffectiveItemHeight + lineSpacing : gridState.EffectiveItemWidth + lineSpacing; @@ -544,7 +544,7 @@ namespace Avalonia.Layout int rowIndex = (int)(index / itemsPerLine); int indexInRow = index - (rowIndex * itemsPerLine); - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; Rect bounds = _orientation.MinorMajorRect( indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent), rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent), @@ -556,6 +556,6 @@ namespace Avalonia.Layout private void InvalidateLayout() => InvalidateMeasure(); - private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState).FlowAlgorithm; + private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState!).FlowAlgorithm; } } diff --git a/src/Avalonia.Layout/UniformGridLayoutState.cs b/src/Avalonia.Layout/UniformGridLayoutState.cs index 282bbab1a8..d6b5a30bfc 100644 --- a/src/Avalonia.Layout/UniformGridLayoutState.cs +++ b/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 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. - private ILayoutable _cachedFirstElement; + private ILayoutable? _cachedFirstElement; internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm(); internal double EffectiveItemWidth { get; private set; } diff --git a/src/Avalonia.Layout/Utils/ListUtils.cs b/src/Avalonia.Layout/Utils/ListUtils.cs index eb2480acd3..9ca8bb0525 100644 --- a/src/Avalonia.Layout/Utils/ListUtils.cs +++ b/src/Avalonia.Layout/Utils/ListUtils.cs @@ -23,10 +23,5 @@ namespace Avalonia.Layout.Utils list.AddRange(Enumerable.Repeat(value, size - cur)); } } - - public static void Resize(this List list, int count) - { - Resize(list, count, default); - } } } diff --git a/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs b/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs index 80ccee2114..f068187523 100644 --- a/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs +++ b/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs @@ -6,14 +6,14 @@ namespace Avalonia.Layout public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext { private readonly VirtualizingLayoutContext _virtualizingContext; - private ChildrenCollection _children; + private ChildrenCollection? _children; public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext) { _virtualizingContext = virtualizingContext; } - protected override object LayoutStateCore + protected override object? LayoutStateCore { get => _virtualizingContext.LayoutState; set => _virtualizingContext.LayoutState = value; diff --git a/src/Avalonia.Layout/VirtualizingLayoutContext.cs b/src/Avalonia.Layout/VirtualizingLayoutContext.cs index 079b91a90f..f48c9b0ef2 100644 --- a/src/Avalonia.Layout/VirtualizingLayoutContext.cs +++ b/src/Avalonia.Layout/VirtualizingLayoutContext.cs @@ -43,7 +43,7 @@ namespace Avalonia.Layout /// public abstract class VirtualizingLayoutContext : LayoutContext { - private NonVirtualizingLayoutContext _contextAdapter; + private NonVirtualizingLayoutContext? _contextAdapter; /// /// Gets the number of items in the data. diff --git a/src/Avalonia.Layout/WrapLayout/UvMeasure.cs b/src/Avalonia.Layout/WrapLayout/UvMeasure.cs index e10e6bd0f8..91fa459acb 100644 --- a/src/Avalonia.Layout/WrapLayout/UvMeasure.cs +++ b/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) { diff --git a/src/Avalonia.Layout/WrapLayout/WrapItem.cs b/src/Avalonia.Layout/WrapLayout/WrapItem.cs index 2f0b39a2d1..e823ade6d4 100644 --- a/src/Avalonia.Layout/WrapLayout/WrapItem.cs +++ b/src/Avalonia.Layout/WrapLayout/WrapItem.cs @@ -20,6 +20,6 @@ namespace Avalonia.Layout public UvMeasure? Position { get; internal set; } - public ILayoutable Element { get; internal set; } + public ILayoutable? Element { get; internal set; } } } diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayout.cs b/src/Avalonia.Layout/WrapLayout/WrapLayout.cs index ded2afc3dd..d12a83d1d7 100644 --- a/src/Avalonia.Layout/WrapLayout/WrapLayout.cs +++ b/src/Avalonia.Layout/WrapLayout/WrapLayout.cs @@ -88,7 +88,7 @@ namespace Avalonia.Layout /// protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args) { - var state = (WrapLayoutState)context.LayoutState; + var state = (WrapLayoutState)context.LayoutState!; switch (args.Action) { @@ -126,7 +126,7 @@ namespace Avalonia.Layout var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var position = UvMeasure.Zero; - var state = (WrapLayoutState)context.LayoutState; + var state = (WrapLayoutState)context.LayoutState!; if (state.Orientation != Orientation) { state.SetOrientation(Orientation); @@ -261,7 +261,7 @@ namespace Avalonia.Layout var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var realizationBounds = new UvBounds(Orientation, context.RealizationRect); - var state = (WrapLayoutState)context.LayoutState; + var state = (WrapLayoutState)context.LayoutState!; bool Arrange(WrapItem item, bool isLast = false) { if (item.Measure.HasValue == false) diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs b/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs index 7955ea983c..720ab96830 100644 --- a/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs +++ b/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs @@ -62,8 +62,11 @@ namespace Avalonia.Layout 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; double v = measure.V; measure.V = measure.U; @@ -120,10 +123,10 @@ namespace Avalonia.Layout } 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) { return (totalHeight / itemCount) * _context.ItemCount;