From cc4b8e3ea08037d19ddaf5270eab428da82b28af Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 2 Sep 2022 10:33:06 +0200 Subject: [PATCH 1/4] Fix typo. --- tests/Avalonia.Controls.UnitTests/BorderTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs index 7af7d1cee2..2cb72f148d 100644 --- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs +++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs @@ -78,7 +78,7 @@ namespace Avalonia.Controls.UnitTests } }; - var root = CreatedRoot(1.5, target); + var root = CreateRoot(1.5, target); root.LayoutManager.ExecuteInitialLayoutPass(); @@ -101,7 +101,7 @@ namespace Avalonia.Controls.UnitTests } }; - var root = CreatedRoot(1.5, target); + var root = CreateRoot(1.5, target); root.LayoutManager.ExecuteInitialLayoutPass(); @@ -111,7 +111,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(104, 104), target.DesiredSize); } - private static TestRoot CreatedRoot( + private static TestRoot CreateRoot( double scaling, Control child, Size? constraint = null) From ac27138120f4b554c61c9e44473713acb0c14a7b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 2 Sep 2022 12:28:56 +0200 Subject: [PATCH 2/4] Add failing tests for #8866. --- .../BorderTests.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs index 2cb72f148d..e9ddc8e8a3 100644 --- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs +++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs @@ -111,6 +111,57 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(104, 104), target.DesiredSize); } + [Fact] + public void Measure_Arranges_Child_To_Rounded_BorderThickness() + { + Canvas child; + var target = new Border + { + BorderThickness = new Thickness(1), + Width = 82, + Height = 82, + Child = child = new Canvas(), + }; + + var root = CreateRoot(1.5, target); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + // - 1 pixel border thickness is rounded up to 1.3333; for both sides it is 2.6666 + // - Size of 82 needs no rounding + // - Minus border thickness, space for child is 82 - 2.6666 = 79.3333 + Assert.Equal(1.3333, child.Bounds.Left, 3); + Assert.Equal(1.3333, child.Bounds.Top, 3); + Assert.Equal(79.3333, child.Bounds.Width, 3); + Assert.Equal(79.3333, child.Bounds.Height, 3); + } + + [Fact] + public void Measure_Arranges_Child_With_Rounded_Margin() + { + Border child; + var target = new Border + { + Width = 220, + Height = 220, + Child = child = new Border + { + Margin = new Thickness(0, 25, 25, 25), + }, + }; + + var root = CreateRoot(1.5, target); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + // - 25 margin gets rounded up to 25.3333 + // - Size of 220 needs no rounding + Assert.Equal(0, child.Bounds.Left, 3); + Assert.Equal(25.3333, child.Bounds.Top, 3); + Assert.Equal(194.6666, child.Bounds.Width, 3); + Assert.Equal(169.3333, child.Bounds.Height, 3); + } + private static TestRoot CreateRoot( double scaling, Control child, From 76747d32b4cd04ef6cce8718120f28f805a865eb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 2 Sep 2022 12:32:51 +0200 Subject: [PATCH 3/4] Handle FP errors in RoundLayoutValueUp. If the `value` passed to `RoundLayoutValueUp` has a floating point precision error (e.g. 79.333333333333343 in the test case) then when it's multiplied by `dpiScale` and rounded up, it will be rounded up to the a value one greater than it should be. Fix this by rounding the initial value to an arbitrary precision of 8 digits. Fixes #8866 --- src/Avalonia.Base/Layout/LayoutHelper.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Avalonia.Base/Layout/LayoutHelper.cs b/src/Avalonia.Base/Layout/LayoutHelper.cs index 404d19906a..0851dbaea9 100644 --- a/src/Avalonia.Base/Layout/LayoutHelper.cs +++ b/src/Avalonia.Base/Layout/LayoutHelper.cs @@ -251,6 +251,17 @@ namespace Avalonia.Layout { double newValue; + // Round the value to avoid FP errors. This is needed because if `value` has a floating + // point precision error (e.g. 79.333333333333343) then when it's multiplied by + // `dpiScale` and rounded up, it will be rounded up to a value one greater than it + // should be. +#if NET6_0_OR_GREATER + value = Math.Round(value, 8, MidpointRounding.ToZero); +#else + // MidpointRounding.ToZero isn't available in netstandard2.0. + value = Math.Truncate(value * 1e8) / 1e8; +#endif + // If DPI == 1, don't use DPI-aware rounding. if (!MathUtilities.IsOne(dpiScale)) { From 8d9f39d3b3bdbc97c52bb91b30754e159e20fc0c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 2 Sep 2022 13:19:45 +0200 Subject: [PATCH 4/4] Fix copypasta. Fixes #8867. --- src/Avalonia.Controls/Utils/BorderRenderHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs index ecc7cd98bb..eb9f38894d 100644 --- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs +++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs @@ -61,7 +61,7 @@ namespace Avalonia.Controls.Utils _backgroundGeometryCache = null; } - if (boundRect.Width != 0 && innerRect.Height != 0) + if (boundRect.Width != 0 && boundRect.Height != 0) { var borderGeometryKeypoints = new BorderGeometryKeypoints(boundRect, borderThickness, cornerRadius, false);