Browse Source

Fix BringDescendantIntoView with respect to margins (#19544)

* remove margin deflation in BringDescendantIntoView and update related test to test  cases with margin and no margin

* create transform relative to presenter, instead of child.

* update tests

* fix tests
pull/19583/head
Emmanuel Hansen 5 months ago
committed by GitHub
parent
commit
267d2470cc
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  2. 80
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
  3. 2
      tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

6
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -251,14 +251,16 @@ namespace Avalonia.Controls.Presenters
return scrollable.BringIntoView(control, targetRect);
}
var transform = target.TransformToVisual(Child);
var transform = target.TransformToVisual(this);
if (transform == null)
{
return false;
}
var rectangle = targetRect.TransformToAABB(transform.Value).Deflate(new Thickness(Child.Margin.Left, Child.Margin.Top, 0, 0));
transform *= Matrix.CreateTranslation(Offset);
var rectangle = targetRect.TransformToAABB(transform.Value);
Rect viewport = new Rect(Offset.X, Offset.Y, Viewport.Width, Viewport.Height);
double minX = ComputeScrollOffsetWithMinimalScroll(viewport.Left, viewport.Right, rectangle.Left, rectangle.Right);

80
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.UnitTests;
using Xunit;
@ -359,6 +360,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
{
Width = 100,
Height = 100,
CanVerticallyScroll = true,
CanHorizontallyScroll = true,
Content = new Border
{
Width = 200,
@ -404,13 +407,68 @@ namespace Avalonia.Controls.UnitTests.Presenters
}
[Fact]
public void BringDescendantIntoView_Should_Not_Move_Child_If_Completely_In_View()
public void BringDescendantIntoView_Should_Move_Child_Even_With_Margin_In_Parent()
{
Border border = new Border
var namescope = new NameScope();
var content = new StackPanel()
{
Orientation = Orientation.Vertical,
Width = 100,
Height = 20
Margin = new Thickness(0, 200),
};
for(int i = 0; i < 100; i++)
{
var child = new Border
{
Width = 100,
Height = 20,
Name = $"Border{i}"
}.RegisterInNameScope(namescope);
content.Children.Add(child);
}
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Width = 200,
Height = 100,
Content = new Decorator
{
Child = content
}
};
NameScope.SetNameScope(target, namescope);
target.UpdateChild();
target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100));
// Border20 is at position 0,600 with bottom at Y=620
var border20 = target.FindControl<Border>("Border20");
target.BringDescendantIntoView(border20, new Rect(border20.Bounds.Size));
// With viewport Height of 100, border becomes fully visible when alligned from the bottom at Offset Y=520, i.e. 620-100
Assert.Equal(new Vector(0, 520), target.Offset);
// Reset stack panel's margin
content.Margin = default;
target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100));
// Border20 is at position 0,800 with bottom at Y=820
var border40 = target.FindControl<Border>("Border40");
target.BringDescendantIntoView(border40, new Rect(border40.Bounds.Size));
// With viewport Height of 100, border becomes fully visible when alligned from the bottom at Offset Y=720, i.e. 820-100
Assert.Equal(new Vector(0, 720), target.Offset);
}
[Fact]
public void BringDescendantIntoView_Should_Not_Move_Child_If_Completely_In_View()
{
var namescope = new NameScope();
var content = new StackPanel()
{
Orientation = Orientation.Vertical,
@ -419,12 +477,12 @@ namespace Avalonia.Controls.UnitTests.Presenters
for(int i = 0; i < 100; i++)
{
// border position will be (0,60)
var child = i == 3 ? border : new Border
var child = new Border
{
Width = 100,
Height = 20,
};
Name = $"Border{i}"
}.RegisterInNameScope(namescope);
content.Children.Add(child);
}
var target = new ScrollContentPresenter
@ -439,11 +497,15 @@ namespace Avalonia.Controls.UnitTests.Presenters
}
};
NameScope.SetNameScope(target, namescope);
target.UpdateChild();
target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100));
target.BringDescendantIntoView(border, new Rect(border.Bounds.Size));
var border3 = target.FindControl<Border>("Border3");
target.BringDescendantIntoView(border3, new Rect(border3.Bounds.Size));
// Border3 is still in view, offset hasn't changed
Assert.Equal(new Vector(0, 0), target.Offset);
}
@ -486,14 +548,17 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.UpdateChild();
target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100));
// move border to above the view port
target.Offset = new Vector(0, 90);
target.Arrange(new Rect(0, 0, 100, 100));
target.BringDescendantIntoView(border, new Rect(border.Bounds.Size));
Assert.Equal(new Vector(0, 60), target.Offset);
// move border to partially above the view port
target.Offset = new Vector(0, 70);
target.Arrange(new Rect(0, 0, 100, 100));
target.BringDescendantIntoView(border, new Rect(border.Bounds.Size));
Assert.Equal(new Vector(0, 60), target.Offset);
@ -540,6 +605,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Arrange(new Rect(0, 0, 100, 100));
// move border such that it's partially above viewport and partially below viewport
target.Offset = new Vector(0, 90);
target.Arrange(new Rect(0, 0, 100, 100));
target.BringDescendantIntoView(border, new Rect(border.Bounds.Size));
Assert.Equal(new Vector(0, 90), target.Offset);

2
tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

@ -1561,7 +1561,7 @@ namespace Avalonia.Controls.UnitTests
[InlineData(0.5d,
0, 7,
0, 7,
0, 9)]
7, 17)]
public void Focused_Container_Is_Positioned_Correctly_when_Container_Size_Change_Causes_It_To_Be_Moved_Into_Visible_Viewport(double bufferFactor,
int firstIndex1, int lastIndex1,
int firstIndex2, int lastIndex2,

Loading…
Cancel
Save