Browse Source

Fix WrapPanel layout jitter with LineSpacing (#21942)

Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
pull/21967/head
Yoyo 2 months ago
committed by GitHub
parent
commit
253354fe24
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 13
      src/Avalonia.Controls/WrapPanel.cs
  2. 52
      tests/Avalonia.Controls.UnitTests/WrapPanelTests.cs

13
src/Avalonia.Controls/WrapPanel.cs

@ -228,6 +228,7 @@ namespace Avalonia.Controls
bool itemExists = false;
bool lineExists = false;
var itemsAlignment = ItemsAlignment;
var useLayoutRounding = UseLayoutRounding;
// If we have infinite space on the U axis, we always use Start alignment to avoid strange behavior
if (uvConstraint.U is double.PositiveInfinity)
itemsAlignment = WrapPanelItemsAlignment.Start;
@ -250,7 +251,7 @@ namespace Avalonia.Controls
itemHeightSet ? itemHeight : child.DesiredSize.Height);
var nextSpacing = itemExists && child.IsVisible ? itemSpacing : 0;
if (MathUtilities.GreaterThan(curLineSize.U + childSize.U + nextSpacing, uvConstraint.U)) // Need to switch to another line
if (GreaterThan(useLayoutRounding, curLineSize.U + childSize.U + nextSpacing, uvConstraint.U)) // Need to switch to another line
{
panelSize.U = Max(curLineSize.U, panelSize.U);
panelSize.V += curLineSize.V + (lineExists ? lineSpacing : 0);
@ -299,6 +300,7 @@ namespace Avalonia.Controls
bool itemExists = false;
bool lineExists = false;
var itemsAlignment = ItemsAlignment;
var useLayoutRounding = UseLayoutRounding;
// If we have infinite space on the U axis, we always use Start alignment to avoid strange behavior
if (uvFinalSize.U is double.PositiveInfinity)
itemsAlignment = WrapPanelItemsAlignment.Start;
@ -311,7 +313,7 @@ namespace Avalonia.Controls
itemHeightSet ? itemHeight : child.DesiredSize.Height);
var nextSpacing = itemExists && child.IsVisible ? itemSpacing : 0;
if (MathUtilities.GreaterThan(curLineSize.U + childSize.U + nextSpacing, uvFinalSize.U)) // Need to switch to another line
if (GreaterThan(useLayoutRounding, curLineSize.U + childSize.U + nextSpacing, uvFinalSize.U)) // Need to switch to another line
{
accumulatedV += lineExists ? lineSpacing : 0; // add spacing to arrange line first
ArrangeLine(curLineSize.V, firstInLine, i);
@ -408,6 +410,13 @@ namespace Avalonia.Controls
}
}
private static bool GreaterThan(bool useLayoutRounding, double value1, double value2)
{
return useLayoutRounding
? value1 > value2 && value1 - value2 > LayoutHelper.LayoutEpsilon
: MathUtilities.GreaterThan(value1, value2);
}
private struct UVSize
{
internal UVSize(Orientation orientation, double width, double height)

52
tests/Avalonia.Controls.UnitTests/WrapPanelTests.cs

@ -261,6 +261,56 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(0, 140, 30, 50), target.Children[3].Bounds);
}
[Theory]
[InlineData(true, 50)]
[InlineData(false, 110)]
public void Measure_Respects_Layout_Rounding_At_Wrap_Boundary(bool useLayoutRounding, double expectedHeight)
{
var target = new WrapPanel
{
UseLayoutRounding = useLayoutRounding,
ItemSpacing = 10,
LineSpacing = 10,
Children =
{
new Border { Width = 45, Height = 50 },
new Border { Width = 45, Height = 50 },
}
};
target.Measure(new Size(100 - LayoutHelper.LayoutEpsilon / 2, double.PositiveInfinity));
Assert.Equal(expectedHeight, target.DesiredSize.Height);
}
[Theory]
[InlineData(true, 0)]
[InlineData(false, 60)]
public void Arrange_Respects_Layout_Rounding_At_Wrap_Boundary(bool useLayoutRounding, double expectedSecondChildY)
{
var target = new WrapPanel
{
UseLayoutRounding = useLayoutRounding,
ItemSpacing = 10,
LineSpacing = 10,
Children =
{
new Border { Width = 45, Height = 50, UseLayoutRounding = false },
new Border
{
Width = 45 + LayoutHelper.LayoutEpsilon / 2,
Height = 50,
UseLayoutRounding = false,
},
}
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 110));
Assert.Equal(expectedSecondChildY, target.Children[1].Bounds.Y);
}
[Fact]
public void Lays_Out_Horizontally_On_Separate_Lines_With_Spacing_Invisible()
{
@ -350,7 +400,7 @@ namespace Avalonia.Controls.UnitTests
new Border // line 1
{
Width = 50,
Height = 50
Height = 50
},
}
};

Loading…
Cancel
Save