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>
public abstract class AttachedLayout : AvaloniaObject
{
public string LayoutId { get; set; }
public string? LayoutId { get; set; }
/// <summary>
/// Occurs when the measurement state (layout) has been invalidated.
/// </summary>
public event EventHandler MeasureInvalidated;
public event EventHandler? MeasureInvalidated;
/// <summary>
/// Occurs when the arrange state (layout) has been invalidated.
/// </summary>
public event EventHandler ArrangeInvalidated;
public event EventHandler? ArrangeInvalidated;
/// <summary>
/// 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>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project>

46
src/Avalonia.Layout/ElementManager.cs

@ -13,10 +13,10 @@ namespace Avalonia.Layout
{
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 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;
}
}

68
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))
{

4
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(

4
src/Avalonia.Layout/LayoutContext.cs

@ -14,7 +14,7 @@ namespace Avalonia.Layout
/// <summary>
/// Gets or sets an object that represents the state of a layout.
/// </summary>
public object LayoutState
public object? LayoutState
{
get => LayoutStateCore;
set => LayoutStateCore = value;
@ -23,6 +23,6 @@ namespace Avalonia.Layout
/// <summary>
/// Implements the behavior of <see cref="LayoutState"/> in a derived or custom LayoutContext.
/// </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;
}
protected override object LayoutStateCore
protected override object? LayoutStateCore
{
get => _nonVirtualizingContext.LayoutState;
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))
{
throw new Exception($"Invalid LayoutScaling returned from {visualRoot.GetType()}");
throw new Exception($"Invalid LayoutScaling returned from {visualRoot!.GetType()}");
}
return result;

1
src/Avalonia.Layout/LayoutQueue.cs

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

2
src/Avalonia.Layout/Layoutable.cs

@ -794,7 +794,7 @@ namespace Avalonia.Layout
/// </summary>
/// <param name="sender">The sender.</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>
/// 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>
public abstract class NonVirtualizingLayoutContext : LayoutContext
{
private VirtualizingLayoutContext _contextAdapter;
private VirtualizingLayoutContext? _contextAdapter;
/// <summary>
/// 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; }
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(
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;
}
}

26
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;
}
}

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 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; }

5
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<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
{
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;

2
src/Avalonia.Layout/VirtualizingLayoutContext.cs

@ -43,7 +43,7 @@ namespace Avalonia.Layout
/// </summary>
public abstract class VirtualizingLayoutContext : LayoutContext
{
private NonVirtualizingLayoutContext _contextAdapter;
private NonVirtualizingLayoutContext? _contextAdapter;
/// <summary>
/// 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)
{

2
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; }
}
}

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

@ -88,7 +88,7 @@ namespace Avalonia.Layout
/// <inheritdoc />
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)

9
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;

Loading…
Cancel
Save