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 a2d53f4f06..26c8464576 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;
@@ -622,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.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..2fe59b49ca 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 static double LayoutEpsilon { get; } = 0.00000153;
+
///
/// Calculates a control's size based on its ,
/// , ,
@@ -82,6 +88,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;
- }
}
}