Browse Source

Implemented UniformGrid. (#1819)

jp2masa 7 years ago
parent
commit
f1e422866a

+ 161 - 0
src/Avalonia.Controls/Primitives/UniformGrid.cs

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

+ 144 - 0
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);
+        }
+    }
+}