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