From ee383659e25fb538511b9bbcfd545ef3060ec28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Mon, 12 Apr 2021 23:30:54 +0200 Subject: [PATCH 1/3] Restore missing DPI correction for GridSplitter behavior. --- src/Avalonia.Controls/GridSplitter.cs | 18 ++++++++++++++---- .../Presenters/ContentPresenter.cs | 14 +------------- src/Avalonia.Layout/LayoutHelper.cs | 19 +++++++++++++++++++ src/Avalonia.Layout/Layoutable.cs | 16 ++-------------- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index a2d53f4f06..a38ee739bc 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -609,10 +609,20 @@ namespace Avalonia.Controls private void MoveSplitter(double horizontalChange, double verticalChange) { Debug.Assert(_resizeData != null, "_resizeData should not be null when calling MoveSplitter"); - - // Calculate the offset to adjust the splitter. - var delta = _resizeData.ResizeDirection == GridResizeDirection.Columns ? horizontalChange : verticalChange; - + + // Calculate the offset to adjust the splitter. If layout rounding is enabled, we + // need to round to an integer physical pixel value to avoid round-ups of children that + // expand the bounds of the Grid. In practice this only happens in high dpi because + // horizontal/vertical offsets here are never fractional (they correspond to mouse movement + // across logical pixels). Rounding error only creeps in when converting to a physical + // display with something other than the logical 96 dpi. + double delta = _resizeData.ResizeDirection == GridResizeDirection.Columns ? horizontalChange : verticalChange; + + if (UseLayoutRounding) + { + delta = LayoutHelper.RoundLayoutValue(delta, LayoutHelper.GetLayoutScale(this)); + } + DefinitionBase definition1 = _resizeData.Definition1; DefinitionBase definition2 = _resizeData.Definition2; diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index b28927c969..472727823a 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -378,7 +378,7 @@ namespace Avalonia.Controls.Presenters var useLayoutRounding = UseLayoutRounding; var availableSize = finalSize; var sizeForChild = availableSize; - var scale = GetLayoutScale(); + var scale = LayoutHelper.GetLayoutScale(this); var originX = offset.X; var originY = offset.Y; @@ -462,18 +462,6 @@ namespace Avalonia.Controls.Presenters PseudoClasses.Set(":empty", Content is null); } - private double GetLayoutScale() - { - var result = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0; - - if (result == 0 || double.IsNaN(result) || double.IsInfinity(result)) - { - throw new Exception($"Invalid LayoutScaling returned from {VisualRoot.GetType()}"); - } - - return result; - } - private void TemplatedParentChanged(AvaloniaPropertyChangedEventArgs e) { var host = e.NewValue as IContentPresenterHost; diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index 3708748ad1..8ad972059f 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -82,6 +82,25 @@ namespace Avalonia.Layout InnerInvalidateMeasure(control); } + /// + /// Obtains layout scale of the given control. + /// + /// The control. + /// Thrown when control has no root or returned layout scaling is invalid. + public static double GetLayoutScale(ILayoutable control) + { + var visualRoot = control.VisualRoot; + + var result = (visualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0; + + if (result == 0 || double.IsNaN(result) || double.IsInfinity(result)) + { + throw new Exception($"Invalid LayoutScaling returned from {visualRoot.GetType()}"); + } + + return result; + } + /// /// Rounds a size to integer values for layout purposes, compensating for high DPI screen /// coordinates. diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index aca2965ea6..a1d00017ed 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -590,7 +590,7 @@ namespace Avalonia.Layout if (UseLayoutRounding) { - var scale = GetLayoutScale(); + var scale = LayoutHelper.GetLayoutScale(this); width = LayoutHelper.RoundLayoutValue(width, scale); height = LayoutHelper.RoundLayoutValue(height, scale); } @@ -652,7 +652,7 @@ namespace Avalonia.Layout var horizontalAlignment = HorizontalAlignment; var verticalAlignment = VerticalAlignment; var size = availableSizeMinusMargins; - var scale = GetLayoutScale(); + var scale = LayoutHelper.GetLayoutScale(this); var useLayoutRounding = UseLayoutRounding; if (horizontalAlignment != HorizontalAlignment.Stretch) @@ -833,17 +833,5 @@ namespace Avalonia.Layout { return new Size(Math.Max(size.Width, 0), Math.Max(size.Height, 0)); } - - private double GetLayoutScale() - { - var result = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0; - - if (result == 0 || double.IsNaN(result) || double.IsInfinity(result)) - { - throw new Exception($"Invalid LayoutScaling returned from {VisualRoot.GetType()}"); - } - - return result; - } } } From 3ac615c4e31e2cc96ff76c67f0d06b6daaf285d3 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 18 Apr 2021 17:06:12 +0200 Subject: [PATCH 2/3] Make GritSplitter calculations match the ones in WPF. --- src/Avalonia.Base/Utilities/MathUtilities.cs | 15 +++++++++++++++ src/Avalonia.Controls/GridSplitter.cs | 4 ++-- src/Avalonia.Layout/LayoutHelper.cs | 6 ++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs index 446b366dc8..1ea369954a 100644 --- a/src/Avalonia.Base/Utilities/MathUtilities.cs +++ b/src/Avalonia.Base/Utilities/MathUtilities.cs @@ -30,6 +30,21 @@ namespace Avalonia.Utilities return (-eps < delta) && (eps > delta); } + /// + /// AreClose - Returns whether or not two doubles are "close". That is, whether or + /// not they are within epsilon of each other. + /// + /// The first double to compare. + /// The second double to compare. + /// The fixed epsilon value used to compare. + public static bool AreClose(double value1, double value2, double eps) + { + //in case they are Infinities (then epsilon check does not work) + if (value1 == value2) return true; + double delta = value1 - value2; + return (-eps < delta) && (eps > delta); + } + /// /// AreClose - Returns whether or not two floats are "close". That is, whether or /// not they are within epsilon of each other. diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index a38ee739bc..26c8464576 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -632,11 +632,11 @@ namespace Avalonia.Controls double actualLength2 = GetActualLength(definition2); // When splitting, Check to see if the total pixels spanned by the definitions - // is the same asbefore starting resize. If not cancel the drag + // is the same as before starting resize. If not cancel the drag. if (_resizeData.SplitBehavior == SplitBehavior.Split && !MathUtilities.AreClose( actualLength1 + actualLength2, - _resizeData.OriginalDefinition1ActualLength + _resizeData.OriginalDefinition2ActualLength)) + _resizeData.OriginalDefinition1ActualLength + _resizeData.OriginalDefinition2ActualLength, LayoutHelper.LayoutEpsilon)) { CancelResize(); diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index 8ad972059f..35eae0f54c 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -9,6 +9,12 @@ namespace Avalonia.Layout /// public static class LayoutHelper { + /// + /// Epsilon value used for certain layout calculations. + /// Based on the value in WPF LayoutDoubleUtil. + /// + public const double LayoutEpsilon = 0.00000153; + /// /// Calculates a control's size based on its , /// , , From c2790472b5d8c4f7b753e65c4e2c1c2b1d268477 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 19 Apr 2021 11:24:30 +0200 Subject: [PATCH 3/3] Made LayoutEpsilon a property. --- src/Avalonia.Layout/LayoutHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index 35eae0f54c..2fe59b49ca 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -13,7 +13,7 @@ namespace Avalonia.Layout /// Epsilon value used for certain layout calculations. /// Based on the value in WPF LayoutDoubleUtil. /// - public const double LayoutEpsilon = 0.00000153; + public static double LayoutEpsilon { get; } = 0.00000153; /// /// Calculates a control's size based on its ,