Browse Source

Added failing test for ScrollViewer jumpiness.

pull/11252/head
Steven Kirk 3 years ago
parent
commit
42bd366bc1
  1. 122
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
  2. 10
      tests/Avalonia.UnitTests/MouseTestHelper.cs

122
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq;
using Xunit;
@ -12,6 +15,8 @@ namespace Avalonia.Controls.UnitTests
{
public class ScrollViewerTests
{
private readonly MouseTestHelper _mouse = new();
[Fact]
public void Content_Is_Created()
{
@ -249,6 +254,73 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Vector(20, 20), target.Offset);
}
[Fact]
public void Scroll_Does_Not_Jump_When_Viewport_Becomes_Smaller_While_Dragging_ScrollBar_Thumb()
{
var content = new TestContent
{
MeasureSize = new Size(1000, 10000),
};
var target = new ScrollViewer
{
Template = new FuncControlTemplate<ScrollViewer>(CreateTemplate),
Content = content,
};
var root = new TestRoot(target);
root.LayoutManager.ExecuteInitialLayoutPass();
Assert.Equal(new Size(1000, 10000), target.Extent);
Assert.Equal(new Size(1000, 1000), target.Viewport);
// We're working in absolute coordinates (i.e. relative to the root) and clicking on
// the center of the vertical thumb.
var thumb = GetVerticalThumb(target);
var p = GetRootPoint(thumb, thumb.Bounds.Center);
// Press the mouse button in the center of the thumb.
_mouse.Down(thumb, position: p);
root.LayoutManager.ExecuteLayoutPass();
// Drag the thumb down 300 pixels.
_mouse.Move(thumb, p += new Vector(0, 300));
root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(new Vector(0, 3000), target.Offset);
Assert.Equal(300, thumb.Bounds.Top);
// Now the extent changes from 10,000 to 5000.
content.MeasureSize /= 2;
content.InvalidateMeasure();
root.LayoutManager.ExecuteLayoutPass();
// Due to the extent change, the thumb moves down but the value remains the same.
Assert.Equal(600, thumb.Bounds.Top);
Assert.Equal(new Vector(0, 3000), target.Offset);
// Drag the thumb down another 100 pixels.
_mouse.Move(thumb, p += new Vector(0, 100));
root.LayoutManager.ExecuteLayoutPass();
// The drag should not cause the offset/thumb to jump *up* to the current absolute
// mouse position, i.e. it should move down in the direction of the drag even if the
// absolute mouse position is now above the thumb.
Assert.Equal(700, thumb.Bounds.Top);
Assert.Equal(new Vector(0, 3500), target.Offset);
}
private Point GetRootPoint(Visual control, Point p)
{
if (control.GetVisualRoot() is Visual root &&
control.TransformToVisual(root) is Matrix m)
{
return p.Transform(m);
}
throw new InvalidOperationException("Could not get the point in root coordinates.");
}
private Control CreateTemplate(ScrollViewer control, INameScope scope)
{
return new Grid
@ -273,6 +345,7 @@ namespace Avalonia.Controls.UnitTests
{
Name = "PART_HorizontalScrollBar",
Orientation = Orientation.Horizontal,
Template = new FuncControlTemplate<ScrollBar>(CreateScrollBarTemplate),
[~ScrollBar.VisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty],
[Grid.RowProperty] = 1,
}.RegisterInNameScope(scope),
@ -280,6 +353,7 @@ namespace Avalonia.Controls.UnitTests
{
Name = "PART_VerticalScrollBar",
Orientation = Orientation.Vertical,
Template = new FuncControlTemplate<ScrollBar>(CreateScrollBarTemplate),
[~ScrollBar.VisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty],
[Grid.ColumnProperty] = 1,
}.RegisterInNameScope(scope),
@ -287,6 +361,44 @@ namespace Avalonia.Controls.UnitTests
};
}
private Control CreateScrollBarTemplate(ScrollBar scrollBar, INameScope scope)
{
return new Border
{
Child = new Track
{
Name = "track",
IsDirectionReversed = true,
[!Track.MinimumProperty] = scrollBar[!RangeBase.MinimumProperty],
[!Track.MaximumProperty] = scrollBar[!RangeBase.MaximumProperty],
[!!Track.ValueProperty] = scrollBar[!!RangeBase.ValueProperty],
[!Track.ViewportSizeProperty] = scrollBar[!ScrollBar.ViewportSizeProperty],
[!Track.OrientationProperty] = scrollBar[!ScrollBar.OrientationProperty],
Thumb = new Thumb
{
Template = new FuncControlTemplate<Thumb>(CreateThumbTemplate),
},
}.RegisterInNameScope(scope),
};
}
private static Control CreateThumbTemplate(Thumb control, INameScope scope)
{
return new Border
{
Background = Brushes.Gray,
};
}
private Thumb GetVerticalThumb(ScrollViewer target)
{
var scrollbar = Assert.IsType<ScrollBar>(
target.GetTemplateChildren().FirstOrDefault(x => x.Name == "PART_VerticalScrollBar"));
var track = Assert.IsType<Track>(
scrollbar.GetTemplateChildren().FirstOrDefault(x => x.Name == "track"));
return Assert.IsType<Thumb>(track.Thumb);
}
private static void InitializeScrollViewer(ScrollViewer target)
{
target.ApplyTemplate();
@ -295,5 +407,15 @@ namespace Avalonia.Controls.UnitTests
presenter.AttachToScrollViewer();
presenter.UpdateChild();
}
private class TestContent : Control
{
public Size MeasureSize { get; set; } = new Size(1000, 2000);
protected override Size MeasureOverride(Size availableSize)
{
return MeasureSize;
}
}
}
}

10
tests/Avalonia.UnitTests/MouseTestHelper.cs

@ -59,7 +59,7 @@ namespace Avalonia.UnitTests
{
_pressedButton = mouseButton;
_pointer.Capture((IInputElement)target);
source.RaiseEvent(new PointerPressedEventArgs(source, _pointer, (Visual)source, position, Timestamp(), props,
source.RaiseEvent(new PointerPressedEventArgs(source, _pointer, GetRoot(target), position, Timestamp(), props,
modifiers, clickCount));
}
}
@ -68,7 +68,7 @@ namespace Avalonia.UnitTests
public void Move(Interactive target, Interactive source, in Point position, KeyModifiers modifiers = default)
{
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position,
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, GetRoot(target), position,
Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), modifiers));
}
@ -88,7 +88,7 @@ namespace Avalonia.UnitTests
);
if (ButtonCount(props) == 0)
{
target.RaiseEvent(new PointerReleasedEventArgs(source, _pointer, (Visual)target, position,
target.RaiseEvent(new PointerReleasedEventArgs(source, _pointer, GetRoot(target), position,
Timestamp(), props, modifiers, _pressedButton));
_pointer.Capture(null);
}
@ -131,5 +131,9 @@ namespace Avalonia.UnitTests
Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), KeyModifiers.None));
}
private Visual GetRoot(Interactive source)
{
return ((source as Visual)?.GetVisualRoot() as Visual) ?? (Visual)source;
}
}
}

Loading…
Cancel
Save