|
@@ -110,6 +110,12 @@ namespace Avalonia.Layout
|
|
|
public static readonly StyledProperty<double> MinRowSpacingProperty =
|
|
|
AvaloniaProperty.Register<UniformGridLayout, double>(nameof(MinRowSpacing));
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Defines the <see cref="MaximumRowsOrColumnsProperty"/> property.
|
|
|
+ /// </summary>
|
|
|
+ public static readonly StyledProperty<int> MaximumRowsOrColumnsProperty =
|
|
|
+ AvaloniaProperty.Register<UniformGridLayout, int>(nameof(MinItemWidth));
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Defines the <see cref="Orientation"/> property.
|
|
|
/// </summary>
|
|
@@ -123,6 +129,7 @@ namespace Avalonia.Layout
|
|
|
private double _minColumnSpacing;
|
|
|
private UniformGridLayoutItemsJustification _itemsJustification;
|
|
|
private UniformGridLayoutItemsStretch _itemsStretch;
|
|
|
+ private int _maximumRowsOrColumns = int.MaxValue;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="UniformGridLayout"/> class.
|
|
@@ -219,6 +226,15 @@ namespace Avalonia.Layout
|
|
|
set => SetValue(MinRowSpacingProperty, value);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the maximum row or column count.
|
|
|
+ /// </summary>
|
|
|
+ public int MaximumRowsOrColumns
|
|
|
+ {
|
|
|
+ get => GetValue(MaximumRowsOrColumnsProperty);
|
|
|
+ set => SetValue(MaximumRowsOrColumnsProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets or sets the axis along which items are laid out.
|
|
|
/// </summary>
|
|
@@ -269,15 +285,17 @@ namespace Avalonia.Layout
|
|
|
{
|
|
|
var gridState = (UniformGridLayoutState)context.LayoutState;
|
|
|
var lastExtent = gridState.FlowAlgorithm.LastExtent;
|
|
|
- int itemsPerLine = Math.Max(1, (int)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context)));
|
|
|
- double majorSize = (itemsCount / itemsPerLine) * GetMajorSizeWithSpacing(context);
|
|
|
- double realizationWindowStartWithinExtent = _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent);
|
|
|
+ var itemsPerLine = Math.Min( // note use of unsigned ints
|
|
|
+ Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
|
|
|
+ Math.Max(1u, (uint)_maximumRowsOrColumns));
|
|
|
+ var majorSize = (itemsCount / itemsPerLine) * GetMajorSizeWithSpacing(context);
|
|
|
+ var realizationWindowStartWithinExtent = _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent);
|
|
|
if ((realizationWindowStartWithinExtent + _orientation.MajorSize(realizationRect)) >= 0 && realizationWindowStartWithinExtent <= majorSize)
|
|
|
{
|
|
|
double offset = Math.Max(0.0, _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent));
|
|
|
int anchorRowIndex = (int)(offset / GetMajorSizeWithSpacing(context));
|
|
|
|
|
|
- anchorIndex = Math.Max(0, Math.Min(itemsCount - 1, anchorRowIndex * itemsPerLine));
|
|
|
+ anchorIndex = (int)Math.Max(0, Math.Min(itemsCount - 1, anchorRowIndex * itemsPerLine));
|
|
|
bounds = GetLayoutRectForDataIndex(availableSize, anchorIndex, lastExtent, context);
|
|
|
}
|
|
|
}
|
|
@@ -299,7 +317,9 @@ namespace Avalonia.Layout
|
|
|
int count = context.ItemCount;
|
|
|
if (targetIndex >= 0 && targetIndex < count)
|
|
|
{
|
|
|
- int itemsPerLine = Math.Max(1, (int)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context)));
|
|
|
+ int itemsPerLine = (int)Math.Min( // note use of unsigned ints
|
|
|
+ Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
|
|
|
+ Math.Max(1u, _maximumRowsOrColumns));
|
|
|
int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine;
|
|
|
index = indexOfFirstInLine;
|
|
|
var state = context.LayoutState as UniformGridLayoutState;
|
|
@@ -329,17 +349,21 @@ namespace Avalonia.Layout
|
|
|
// Constants
|
|
|
int itemsCount = context.ItemCount;
|
|
|
double availableSizeMinor = _orientation.Minor(availableSize);
|
|
|
- int itemsPerLine = Math.Max(1, !double.IsInfinity(availableSizeMinor) ?
|
|
|
- (int)(availableSizeMinor / GetMinorSizeWithSpacing(context)) : itemsCount);
|
|
|
+ int itemsPerLine =
|
|
|
+ (int)Math.Min( // note use of unsigned ints
|
|
|
+ Math.Max(1u, !double.IsInfinity(availableSizeMinor)
|
|
|
+ ? (uint)(availableSizeMinor / GetMinorSizeWithSpacing(context))
|
|
|
+ : (uint)itemsCount),
|
|
|
+ Math.Max(1u, _maximumRowsOrColumns));
|
|
|
double lineSize = GetMajorSizeWithSpacing(context);
|
|
|
|
|
|
if (itemsCount > 0)
|
|
|
{
|
|
|
_orientation.SetMinorSize(
|
|
|
ref extent,
|
|
|
- !double.IsInfinity(availableSizeMinor) ?
|
|
|
+ !double.IsInfinity(availableSizeMinor) && _itemsStretch == UniformGridLayoutItemsStretch.Fill ?
|
|
|
availableSizeMinor :
|
|
|
- Math.Max(0.0, itemsCount * GetMinorSizeWithSpacing(context) - (double)MinItemSpacing));
|
|
|
+ Math.Max(0.0, itemsPerLine * GetMinorSizeWithSpacing(context) - (double)MinItemSpacing));
|
|
|
_orientation.SetMajorSize(
|
|
|
ref extent,
|
|
|
Math.Max(0.0, (itemsCount / itemsPerLine) * lineSize - (double)LineSpacing));
|
|
@@ -398,7 +422,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;
|
|
|
- gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing);
|
|
|
+ gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns);
|
|
|
|
|
|
var desiredSize = GetFlowAlgorithm(context).Measure(
|
|
|
availableSize,
|
|
@@ -406,6 +430,7 @@ namespace Avalonia.Layout
|
|
|
true,
|
|
|
MinItemSpacing,
|
|
|
LineSpacing,
|
|
|
+ _maximumRowsOrColumns,
|
|
|
_orientation.ScrollOrientation,
|
|
|
LayoutId);
|
|
|
|
|
@@ -421,6 +446,7 @@ namespace Avalonia.Layout
|
|
|
var value = GetFlowAlgorithm(context).Arrange(
|
|
|
finalSize,
|
|
|
context,
|
|
|
+ true,
|
|
|
(FlowLayoutAlgorithm.LineAlignment)_itemsJustification,
|
|
|
LayoutId);
|
|
|
return new Size(value.Width, value.Height);
|
|
@@ -471,6 +497,10 @@ namespace Avalonia.Layout
|
|
|
{
|
|
|
_minItemHeight = (double)args.NewValue;
|
|
|
}
|
|
|
+ else if (args.Property == MaximumRowsOrColumnsProperty)
|
|
|
+ {
|
|
|
+ _maximumRowsOrColumns = (int)args.NewValue;
|
|
|
+ }
|
|
|
|
|
|
InvalidateLayout();
|
|
|
}
|
|
@@ -499,7 +529,9 @@ namespace Avalonia.Layout
|
|
|
Rect lastExtent,
|
|
|
VirtualizingLayoutContext context)
|
|
|
{
|
|
|
- int itemsPerLine = Math.Max(1, (int)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context)));
|
|
|
+ int itemsPerLine = (int)Math.Min( //note use of unsigned ints
|
|
|
+ Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
|
|
|
+ Math.Max(1u, _maximumRowsOrColumns));
|
|
|
int rowIndex = (int)(index / itemsPerLine);
|
|
|
int indexInRow = index - (rowIndex * itemsPerLine);
|
|
|
|