From d39cb62a5d87fc38f70d41878ba8cd4abda6d2ce Mon Sep 17 00:00:00 2001 From: Soar360 Date: Fri, 5 Dec 2025 23:21:24 +0800 Subject: [PATCH] UniformGrid: Round measured cell sizes when UseLayoutRounding is enabled (#19959) * Implement layout rounding for child sizes Added layout rounding for child measurements in UniformGrid. * Remove comment on child measurement in UniformGrid Removed comment about measuring children in UniformGrid. * Improved layout rounding logic for UniformGrid * Grid_Ensures_Consistent_Cell_Width_When_UseLayoutRounding --- .../Primitives/UniformGrid.cs | 17 ++++++ .../Primitives/UniformGridTests.cs | 59 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/UniformGrid.cs b/src/Avalonia.Controls/Primitives/UniformGrid.cs index a65b0fdb9e..57752c0d28 100644 --- a/src/Avalonia.Controls/Primitives/UniformGrid.cs +++ b/src/Avalonia.Controls/Primitives/UniformGrid.cs @@ -1,4 +1,6 @@ using System; +using Avalonia.Layout; +using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { @@ -116,6 +118,13 @@ namespace Avalonia.Controls.Primitives } } + if (UseLayoutRounding) + { + var scale = LayoutHelper.GetLayoutScale(this); + maxWidth = LayoutHelper.RoundLayoutValue(maxWidth, scale); + maxHeight = LayoutHelper.RoundLayoutValue(maxHeight, scale); + } + var totalWidth = maxWidth * _columns + ColumnSpacing * (_columns - 1); var totalHeight = maxHeight * _rows + RowSpacing * (_rows - 1); @@ -136,6 +145,14 @@ namespace Avalonia.Controls.Primitives var width = Math.Max((finalSize.Width - (_columns - 1) * columnSpacing) / _columns, 0); var height = Math.Max((finalSize.Height - (_rows - 1) * rowSpacing) / _rows, 0); + // If layout rounding is enabled, round the per-cell unit size to integral device units. + if (UseLayoutRounding) + { + var scale = LayoutHelper.GetLayoutScale(this); + width = LayoutHelper.RoundLayoutValue(width, scale); + height = LayoutHelper.RoundLayoutValue(height, scale); + } + foreach (var child in Children) { if (!child.IsVisible) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs index e0c8413eb2..1b87a3ff2f 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Controls.Primitives; using Avalonia.UnitTests; +using Avalonia.Utilities; using Xunit; namespace Avalonia.Controls.UnitTests.Primitives @@ -269,6 +270,62 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(new Size(105, 145), target.Bounds.Size); } + [Fact] + public void Grid_Ensures_Consistent_Cell_Width_When_UseLayoutRounding() + { + // Test scenario: 800x600 resolution, 21 children, 3 rows, 7 columns, 1 pixel spacing + // Verifies that all cells have consistent width when UseLayoutRounding is enabled + var target = new UniformGrid + { + Rows = 3, + Columns = 7, + RowSpacing = 1, + ColumnSpacing = 1, + UseLayoutRounding = true + }; + + // Add 21 children + for (int i = 0; i < 21; i++) + { + target.Children.Add(new Border()); + } + + // Arrange at 800x600 resolution + target.Measure(new Size(800, 600)); + target.Arrange(new Rect(0, 0, 800, 600)); + + // Calculate expected cell width + // Available width = 800, column spacing takes up 6 pixels (7 columns - 1 = 6 gaps) + // Available width for cells = 800 - 6 = 794 + // Width per cell = 794 / 7 = 113.428... + // With layout rounding, this should be rounded to ensure consistent cell sizes + double expectedCellWidth = Math.Round((800 - 6) / 7.0); // 794 / 7 ≈ 113.43 → rounds to 113 + + // Verify all children have the same width + double? firstChildWidth = null; + for (int i = 0; i < target.Children.Count; i++) + { + var child = target.Children[i] as Border; + Assert.NotNull(child); + + if (firstChildWidth == null) + { + firstChildWidth = child.Bounds.Width; + } + else + { + // All children should have the same width (allowing for minor floating point differences) + Assert.True(Math.Abs(child.Bounds.Width - firstChildWidth.Value) < 0.01, + $"Child {i} has width {child.Bounds.Width}, expected {firstChildWidth.Value}"); + } + } + + // Verify the calculated width matches expected + Assert.NotNull(firstChildWidth); + Assert.True(Math.Abs(firstChildWidth.Value - expectedCellWidth) < 0.01, + $"Child width {firstChildWidth.Value} does not match expected {expectedCellWidth}"); + } + /// /// Exposes MeasureOverride for testing inherited classes /// @@ -304,7 +361,7 @@ namespace Avalonia.Controls.UnitTests.Primitives // Expected: (0, 0) Assert.Equal(0, desiredSize.Width); Assert.Equal(0, desiredSize.Height); - + } [Fact]