Skip to content

Commit d39cb62

Browse files
authored
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
1 parent 34b00c5 commit d39cb62

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

src/Avalonia.Controls/Primitives/UniformGrid.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using Avalonia.Layout;
3+
using Avalonia.Utilities;
24

35
namespace Avalonia.Controls.Primitives
46
{
@@ -116,6 +118,13 @@ protected override Size MeasureOverride(Size availableSize)
116118
}
117119
}
118120

121+
if (UseLayoutRounding)
122+
{
123+
var scale = LayoutHelper.GetLayoutScale(this);
124+
maxWidth = LayoutHelper.RoundLayoutValue(maxWidth, scale);
125+
maxHeight = LayoutHelper.RoundLayoutValue(maxHeight, scale);
126+
}
127+
119128
var totalWidth = maxWidth * _columns + ColumnSpacing * (_columns - 1);
120129
var totalHeight = maxHeight * _rows + RowSpacing * (_rows - 1);
121130

@@ -136,6 +145,14 @@ protected override Size ArrangeOverride(Size finalSize)
136145
var width = Math.Max((finalSize.Width - (_columns - 1) * columnSpacing) / _columns, 0);
137146
var height = Math.Max((finalSize.Height - (_rows - 1) * rowSpacing) / _rows, 0);
138147

148+
// If layout rounding is enabled, round the per-cell unit size to integral device units.
149+
if (UseLayoutRounding)
150+
{
151+
var scale = LayoutHelper.GetLayoutScale(this);
152+
width = LayoutHelper.RoundLayoutValue(width, scale);
153+
height = LayoutHelper.RoundLayoutValue(height, scale);
154+
}
155+
139156
foreach (var child in Children)
140157
{
141158
if (!child.IsVisible)

tests/Avalonia.Controls.UnitTests/Primitives/UniformGridTests.cs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using Avalonia.Controls.Primitives;
33
using Avalonia.UnitTests;
4+
using Avalonia.Utilities;
45
using Xunit;
56

67
namespace Avalonia.Controls.UnitTests.Primitives
@@ -269,6 +270,62 @@ public void Grid_Respects_Spacing_When_Invisible_Child_Exists()
269270
Assert.Equal(new Size(105, 145), target.Bounds.Size);
270271
}
271272

273+
[Fact]
274+
public void Grid_Ensures_Consistent_Cell_Width_When_UseLayoutRounding()
275+
{
276+
// Test scenario: 800x600 resolution, 21 children, 3 rows, 7 columns, 1 pixel spacing
277+
// Verifies that all cells have consistent width when UseLayoutRounding is enabled
278+
var target = new UniformGrid
279+
{
280+
Rows = 3,
281+
Columns = 7,
282+
RowSpacing = 1,
283+
ColumnSpacing = 1,
284+
UseLayoutRounding = true
285+
};
286+
287+
// Add 21 children
288+
for (int i = 0; i < 21; i++)
289+
{
290+
target.Children.Add(new Border());
291+
}
292+
293+
// Arrange at 800x600 resolution
294+
target.Measure(new Size(800, 600));
295+
target.Arrange(new Rect(0, 0, 800, 600));
296+
297+
// Calculate expected cell width
298+
// Available width = 800, column spacing takes up 6 pixels (7 columns - 1 = 6 gaps)
299+
// Available width for cells = 800 - 6 = 794
300+
// Width per cell = 794 / 7 = 113.428...
301+
// With layout rounding, this should be rounded to ensure consistent cell sizes
302+
double expectedCellWidth = Math.Round((800 - 6) / 7.0); // 794 / 7 ≈ 113.43 → rounds to 113
303+
304+
// Verify all children have the same width
305+
double? firstChildWidth = null;
306+
for (int i = 0; i < target.Children.Count; i++)
307+
{
308+
var child = target.Children[i] as Border;
309+
Assert.NotNull(child);
310+
311+
if (firstChildWidth == null)
312+
{
313+
firstChildWidth = child.Bounds.Width;
314+
}
315+
else
316+
{
317+
// All children should have the same width (allowing for minor floating point differences)
318+
Assert.True(Math.Abs(child.Bounds.Width - firstChildWidth.Value) < 0.01,
319+
$"Child {i} has width {child.Bounds.Width}, expected {firstChildWidth.Value}");
320+
}
321+
}
322+
323+
// Verify the calculated width matches expected
324+
Assert.NotNull(firstChildWidth);
325+
Assert.True(Math.Abs(firstChildWidth.Value - expectedCellWidth) < 0.01,
326+
$"Child width {firstChildWidth.Value} does not match expected {expectedCellWidth}");
327+
}
328+
272329
/// <summary>
273330
/// Exposes MeasureOverride for testing inherited classes
274331
/// </summary>
@@ -304,7 +361,7 @@ public void Measure_WithRowsAndColumnsZeroAndNonZeroSpacing_ProducesZeroDesiredS
304361
// Expected: (0, 0)
305362
Assert.Equal(0, desiredSize.Width);
306363
Assert.Equal(0, desiredSize.Height);
307-
364+
308365
}
309366

310367
[Fact]

0 commit comments

Comments
 (0)