committed by
Nelson Carrillo
2 changed files with 305 additions and 0 deletions
@ -0,0 +1,161 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Controls.Primitives |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A <see cref="Panel"/> with uniform column and row sizes.
|
||||
|
/// </summary>
|
||||
|
public class UniformGrid : Panel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Rows"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<int> RowsProperty = |
||||
|
AvaloniaProperty.Register<UniformGrid, int>(nameof(Rows)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Columns"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<int> ColumnsProperty = |
||||
|
AvaloniaProperty.Register<UniformGrid, int>(nameof(Columns)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="FirstColumn"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<int> FirstColumnProperty = |
||||
|
AvaloniaProperty.Register<UniformGrid, int>(nameof(FirstColumn)); |
||||
|
|
||||
|
private int _rows; |
||||
|
private int _columns; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Specifies the row count. If set to 0, row count will be calculated automatically.
|
||||
|
/// </summary>
|
||||
|
public int Rows |
||||
|
{ |
||||
|
get => GetValue(RowsProperty); |
||||
|
set => SetValue(RowsProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Specifies the column count. If set to 0, column count will be calculated automatically.
|
||||
|
/// </summary>
|
||||
|
public int Columns |
||||
|
{ |
||||
|
get => GetValue(ColumnsProperty); |
||||
|
set => SetValue(ColumnsProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Specifies, for the first row, the column where the items should start.
|
||||
|
/// </summary>
|
||||
|
public int FirstColumn |
||||
|
{ |
||||
|
get => GetValue(FirstColumnProperty); |
||||
|
set => SetValue(FirstColumnProperty, value); |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
UpdateRowsAndColumns(); |
||||
|
|
||||
|
var maxWidth = 0d; |
||||
|
var maxHeight = 0d; |
||||
|
|
||||
|
var childAvailableSize = new Size(availableSize.Width / _columns, availableSize.Height / _rows); |
||||
|
|
||||
|
foreach (var child in Children) |
||||
|
{ |
||||
|
child.Measure(childAvailableSize); |
||||
|
|
||||
|
if (child.DesiredSize.Width > maxWidth) |
||||
|
{ |
||||
|
maxWidth = child.DesiredSize.Width; |
||||
|
} |
||||
|
|
||||
|
if (child.DesiredSize.Height > maxHeight) |
||||
|
{ |
||||
|
maxHeight = child.DesiredSize.Height; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return new Size(maxWidth * _columns, maxHeight * _rows); |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
var x = FirstColumn; |
||||
|
var y = 0; |
||||
|
|
||||
|
var width = finalSize.Width / _columns; |
||||
|
var height = finalSize.Height / _rows; |
||||
|
|
||||
|
foreach (var child in Children) |
||||
|
{ |
||||
|
if (!child.IsVisible) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
child.Arrange(new Rect(x * width, y * height, width, height)); |
||||
|
|
||||
|
x++; |
||||
|
|
||||
|
if (x >= _columns) |
||||
|
{ |
||||
|
x = 0; |
||||
|
y++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return finalSize; |
||||
|
} |
||||
|
|
||||
|
private void UpdateRowsAndColumns() |
||||
|
{ |
||||
|
_rows = Rows; |
||||
|
_columns = Columns; |
||||
|
|
||||
|
if (FirstColumn >= Columns) |
||||
|
{ |
||||
|
FirstColumn = 0; |
||||
|
} |
||||
|
|
||||
|
var itemCount = FirstColumn; |
||||
|
|
||||
|
foreach (var child in Children) |
||||
|
{ |
||||
|
if (child.IsVisible) |
||||
|
{ |
||||
|
itemCount++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (_rows == 0) |
||||
|
{ |
||||
|
if (_columns == 0) |
||||
|
{ |
||||
|
_rows = _columns = (int)Math.Ceiling(Math.Sqrt(itemCount)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_rows = Math.DivRem(itemCount, _columns, out int rem); |
||||
|
|
||||
|
if (rem != 0) |
||||
|
{ |
||||
|
_rows++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else if (_columns == 0) |
||||
|
{ |
||||
|
_columns = Math.DivRem(itemCount, _rows, out int rem); |
||||
|
|
||||
|
if (rem != 0) |
||||
|
{ |
||||
|
_columns++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,144 @@ |
|||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Controls.UnitTests.Primitives |
||||
|
{ |
||||
|
public class UniformGridTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Grid_Columns_Equals_Rows_For_Auto_Columns_And_Rows() |
||||
|
{ |
||||
|
var target = new UniformGrid() |
||||
|
{ |
||||
|
Children = |
||||
|
{ |
||||
|
new Border { Width = 50, Height = 70 }, |
||||
|
new Border { Width = 30, Height = 50 }, |
||||
|
new Border { Width = 80, Height = 90 } |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
target.Measure(Size.Infinity); |
||||
|
target.Arrange(new Rect(target.DesiredSize)); |
||||
|
|
||||
|
// 2 * 2 grid
|
||||
|
Assert.Equal(new Size(2 * 80, 2 * 90), target.Bounds.Size); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Grid_Expands_Vertically_For_Columns_With_Auto_Rows() |
||||
|
{ |
||||
|
var target = new UniformGrid() |
||||
|
{ |
||||
|
Columns = 2, |
||||
|
Children = |
||||
|
{ |
||||
|
new Border { Width = 50, Height = 70 }, |
||||
|
new Border { Width = 30, Height = 50 }, |
||||
|
new Border { Width = 80, Height = 90 }, |
||||
|
new Border { Width = 20, Height = 30 }, |
||||
|
new Border { Width = 40, Height = 60 } |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
target.Measure(Size.Infinity); |
||||
|
target.Arrange(new Rect(target.DesiredSize)); |
||||
|
|
||||
|
// 2 * 3 grid
|
||||
|
Assert.Equal(new Size(2 * 80, 3 * 90), target.Bounds.Size); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Grid_Extends_For_Columns_And_First_Column_With_Auto_Rows() |
||||
|
{ |
||||
|
var target = new UniformGrid() |
||||
|
{ |
||||
|
Columns = 3, |
||||
|
FirstColumn = 2, |
||||
|
Children = |
||||
|
{ |
||||
|
new Border { Width = 50, Height = 70 }, |
||||
|
new Border { Width = 30, Height = 50 }, |
||||
|
new Border { Width = 80, Height = 90 }, |
||||
|
new Border { Width = 20, Height = 30 }, |
||||
|
new Border { Width = 40, Height = 60 } |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
target.Measure(Size.Infinity); |
||||
|
target.Arrange(new Rect(target.DesiredSize)); |
||||
|
|
||||
|
// 3 * 3 grid
|
||||
|
Assert.Equal(new Size(3 * 80, 3 * 90), target.Bounds.Size); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Grid_Expands_Horizontally_For_Rows_With_Auto_Columns() |
||||
|
{ |
||||
|
var target = new UniformGrid() |
||||
|
{ |
||||
|
Rows = 2, |
||||
|
Children = |
||||
|
{ |
||||
|
new Border { Width = 50, Height = 70 }, |
||||
|
new Border { Width = 30, Height = 50 }, |
||||
|
new Border { Width = 80, Height = 90 }, |
||||
|
new Border { Width = 20, Height = 30 }, |
||||
|
new Border { Width = 40, Height = 60 } |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
target.Measure(Size.Infinity); |
||||
|
target.Arrange(new Rect(target.DesiredSize)); |
||||
|
|
||||
|
// 3 * 2 grid
|
||||
|
Assert.Equal(new Size(3 * 80, 2 * 90), target.Bounds.Size); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Grid_Size_Is_Limited_By_Rows_And_Columns() |
||||
|
{ |
||||
|
var target = new UniformGrid() |
||||
|
{ |
||||
|
Columns = 2, |
||||
|
Rows = 2, |
||||
|
Children = |
||||
|
{ |
||||
|
new Border { Width = 50, Height = 70 }, |
||||
|
new Border { Width = 30, Height = 50 }, |
||||
|
new Border { Width = 80, Height = 90 }, |
||||
|
new Border { Width = 20, Height = 30 }, |
||||
|
new Border { Width = 40, Height = 60 } |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
target.Measure(Size.Infinity); |
||||
|
target.Arrange(new Rect(target.DesiredSize)); |
||||
|
|
||||
|
// 2 * 2 grid
|
||||
|
Assert.Equal(new Size(2 * 80, 2 * 90), target.Bounds.Size); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Not_Visible_Children_Are_Ignored() |
||||
|
{ |
||||
|
var target = new UniformGrid() |
||||
|
{ |
||||
|
Children = |
||||
|
{ |
||||
|
new Border { Width = 50, Height = 70 }, |
||||
|
new Border { Width = 30, Height = 50 }, |
||||
|
new Border { Width = 80, Height = 90, IsVisible = false }, |
||||
|
new Border { Width = 20, Height = 30 }, |
||||
|
new Border { Width = 40, Height = 60 } |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
target.Measure(Size.Infinity); |
||||
|
target.Arrange(new Rect(target.DesiredSize)); |
||||
|
|
||||
|
// 2 * 2 grid
|
||||
|
Assert.Equal(new Size(2 * 50, 2 * 70), target.Bounds.Size); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue