From f1e422866a3c4e04defcdbcd1abca98306c4cc02 Mon Sep 17 00:00:00 2001 From: jp2masa Date: Tue, 21 Aug 2018 00:51:20 +0100 Subject: [PATCH] Implemented UniformGrid. (#1819) --- .../Primitives/UniformGrid.cs | 161 ++++++++++++++++++ .../Primitives/UniformGridTests.cs | 144 ++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 src/Avalonia.Controls/Primitives/UniformGrid.cs create mode 100644 tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs diff --git a/src/Avalonia.Controls/Primitives/UniformGrid.cs b/src/Avalonia.Controls/Primitives/UniformGrid.cs new file mode 100644 index 0000000000..f3580eee10 --- /dev/null +++ b/src/Avalonia.Controls/Primitives/UniformGrid.cs @@ -0,0 +1,161 @@ +using System; + +namespace Avalonia.Controls.Primitives +{ + /// + /// A with uniform column and row sizes. + /// + public class UniformGrid : Panel + { + /// + /// Defines the property. + /// + public static readonly StyledProperty RowsProperty = + AvaloniaProperty.Register(nameof(Rows)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColumnsProperty = + AvaloniaProperty.Register(nameof(Columns)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FirstColumnProperty = + AvaloniaProperty.Register(nameof(FirstColumn)); + + private int _rows; + private int _columns; + + /// + /// Specifies the row count. If set to 0, row count will be calculated automatically. + /// + public int Rows + { + get => GetValue(RowsProperty); + set => SetValue(RowsProperty, value); + } + + /// + /// Specifies the column count. If set to 0, column count will be calculated automatically. + /// + public int Columns + { + get => GetValue(ColumnsProperty); + set => SetValue(ColumnsProperty, value); + } + + /// + /// Specifies, for the first row, the column where the items should start. + /// + 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++; + } + } + } + } +} diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs new file mode 100644 index 0000000000..340bd09611 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs @@ -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); + } + } +}