Browse Source

Update tests from UWP tests.

UWP tests are at: https://github.com/grokys/EffectiveBoundsTestsUWP

Try to make the Avalonia tests match them as closely as possible in order to easily port them across.
pull/4173/head
Steven Kirk 6 years ago
parent
commit
b5da0310f1
  1. 20
      src/Avalonia.Layout/LayoutManager.cs
  2. 400
      tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs

20
src/Avalonia.Layout/LayoutManager.cs

@ -199,7 +199,9 @@ namespace Avalonia.Layout
void ILayoutManager.RegisterEffectiveViewportListener(ILayoutable control) void ILayoutManager.RegisterEffectiveViewportListener(ILayoutable control)
{ {
_effectiveViewportChangedListeners ??= new List<EffectiveViewportChangedListener>(); _effectiveViewportChangedListeners ??= new List<EffectiveViewportChangedListener>();
_effectiveViewportChangedListeners.Add(new EffectiveViewportChangedListener(control)); _effectiveViewportChangedListeners.Add(new EffectiveViewportChangedListener(
control,
CalculateEffectiveViewport(control)));
} }
void ILayoutManager.UnregisterEffectiveViewportListener(ILayoutable control) void ILayoutManager.UnregisterEffectiveViewportListener(ILayoutable control)
@ -331,8 +333,7 @@ namespace Avalonia.Layout
for (var i = 0; i < _effectiveViewportChangedListeners.Count; ++i) for (var i = 0; i < _effectiveViewportChangedListeners.Count; ++i)
{ {
var l = _effectiveViewportChangedListeners[i]; var l = _effectiveViewportChangedListeners[i];
var viewport = new Rect(0, 0, double.PositiveInfinity, double.PositiveInfinity); var viewport = CalculateEffectiveViewport(l.Listener);
CalculateEffectiveViewport(l.Listener, ref viewport);
if (viewport != l.Viewport) if (viewport != l.Viewport)
{ {
@ -345,6 +346,13 @@ namespace Avalonia.Layout
return startCount != _toMeasure.Count + _toMeasure.Count; return startCount != _toMeasure.Count + _toMeasure.Count;
} }
private Rect CalculateEffectiveViewport(IVisual control)
{
var viewport = new Rect(0, 0, double.PositiveInfinity, double.PositiveInfinity);
CalculateEffectiveViewport(control, ref viewport);
return viewport;
}
private void CalculateEffectiveViewport(IVisual control, ref Rect viewport) private void CalculateEffectiveViewport(IVisual control, ref Rect viewport)
{ {
if (control.VisualParent is object) if (control.VisualParent is object)
@ -364,12 +372,6 @@ namespace Avalonia.Layout
private readonly struct EffectiveViewportChangedListener private readonly struct EffectiveViewportChangedListener
{ {
public EffectiveViewportChangedListener(ILayoutable listener)
{
Listener = listener;
Viewport = new Rect(double.NaN, double.NaN, double.NaN, double.NaN);
}
public EffectiveViewportChangedListener(ILayoutable listener, Rect viewport) public EffectiveViewportChangedListener(ILayoutable listener, Rect viewport)
{ {
Listener = listener; Listener = listener;

400
tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs

@ -1,7 +1,10 @@
using Avalonia.Controls; using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Media;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Xunit; using Xunit;
@ -10,257 +13,302 @@ namespace Avalonia.Layout.UnitTests
public class LayoutableTests_EffectiveViewportChanged public class LayoutableTests_EffectiveViewportChanged
{ {
[Fact] [Fact]
public void EffectiveViewportChanged_Not_Raised_When_Control_Added_To_Tree() public async Task EffectiveViewportChanged_Not_Raised_When_Control_Added_To_Tree()
{ {
var root = new TestRoot(); await RunOnUIThread.Execute(async () =>
var canvas = new Canvas();
var raised = 0;
canvas.EffectiveViewportChanged += (s, e) =>
{ {
++raised; var root = CreateRoot();
}; var target = new Canvas();
var raised = 0;
target.EffectiveViewportChanged += (s, e) =>
{
++raised;
};
root.Child = canvas; root.Child = target;
Assert.Equal(0, raised); Assert.Equal(0, raised);
});
} }
[Fact] [Fact]
public void EffectiveViewportChanged_Raised_Before_LayoutUpdated() public async Task EffectiveViewportChanged_Raised_Before_LayoutUpdated()
{ {
var root = new TestRoot(); await RunOnUIThread.Execute(async () =>
var canvas = new Canvas();
var raised = 0;
var layoutUpdatedRaised = 0;
canvas.LayoutUpdated += (s, e) =>
{ {
Assert.Equal(1, raised); var root = CreateRoot();
++layoutUpdatedRaised; var target = new Canvas();
}; var raised = 0;
canvas.EffectiveViewportChanged += (s, e) => target.EffectiveViewportChanged += (s, e) =>
{ {
++raised; ++raised;
}; };
root.Child = canvas; root.Child = target;
root.LayoutManager.ExecuteInitialLayoutPass(root);
Assert.Equal(1, layoutUpdatedRaised); await ExecuteInitialLayoutPass(root);
Assert.Equal(1, raised);
Assert.Equal(1, raised);
});
} }
[Fact] [Fact]
public void Invalidating_In_Handler_Causes_Layout_To_Be_Rerun_Before_LayoutUpdated() public async Task Parent_Affects_EffectiveViewport()
{ {
var root = new TestRoot(); await RunOnUIThread.Execute(async () =>
var canvas = new TestCanvas();
var raised = 0;
var layoutUpdatedRaised = 0;
canvas.LayoutUpdated += (s, e) =>
{ {
Assert.Equal(2, canvas.MeasureCount); var root = CreateRoot();
Assert.Equal(2, canvas.ArrangeCount); var target = new Canvas { Width = 100, Height = 100 };
++layoutUpdatedRaised; var parent = new Border { Width = 200, Height = 200, Child = target };
}; var raised = 0;
canvas.EffectiveViewportChanged += (s, e) => root.Child = parent;
{
canvas.InvalidateMeasure();
++raised;
};
root.Child = canvas; target.EffectiveViewportChanged += (s, e) =>
root.LayoutManager.ExecuteInitialLayoutPass(root); {
Assert.Equal(new Rect(-550, -400, 1200, 900), e.EffectiveViewport);
++raised;
};
Assert.Equal(1, raised); await ExecuteInitialLayoutPass(root);
Assert.Equal(1, layoutUpdatedRaised); });
} }
[Fact] [Fact]
public void Viewport_Extends_Beyond_Centered_Control() public async Task Invalidating_In_Handler_Causes_Layout_To_Be_Rerun_Before_LayoutUpdated_Raised()
{ {
var root = new TestRoot await RunOnUIThread.Execute(async () =>
{ {
Width = 1200, var root = CreateRoot();
Height = 900, var target = new TestCanvas();
}; var raised = 0;
var layoutUpdatedRaised = 0;
var canvas = new Canvas root.LayoutUpdated += (s, e) =>
{ {
Width = 52, Assert.Equal(2, target.MeasureCount);
Height = 52, Assert.Equal(2, target.ArrangeCount);
}; ++layoutUpdatedRaised;
var raised = 0; };
canvas.EffectiveViewportChanged += (s, e) => target.EffectiveViewportChanged += (s, e) =>
{ {
Assert.Equal(new Rect(-574, -424, 1200, 900), e.EffectiveViewport); target.InvalidateMeasure();
++raised; ++raised;
}; };
root.Child = canvas; root.Child = target;
root.LayoutManager.ExecuteInitialLayoutPass(root);
await ExecuteInitialLayoutPass(root);
Assert.Equal(1, raised); Assert.Equal(1, raised);
Assert.Equal(1, layoutUpdatedRaised);
});
} }
[Fact] [Fact]
public void Viewport_Extends_Beyond_Nested_Centered_Control() public async Task Viewport_Extends_Beyond_Centered_Control()
{ {
var root = new TestRoot await RunOnUIThread.Execute(async () =>
{ {
Width = 1200, var root = CreateRoot();
Height = 900, var target = new Canvas { Width = 52, Height = 52, };
}; var raised = 0;
var canvas = new Canvas target.EffectiveViewportChanged += (s, e) =>
{ {
Width = 52, Assert.Equal(new Rect(-574, -424, 1200, 900), e.EffectiveViewport);
Height = 52, ++raised;
}; };
var outer = new Border root.Child = target;
{
Width = 100,
Height = 100,
Child = canvas,
};
var raised = 0; await ExecuteInitialLayoutPass(root);
Assert.Equal(1, raised);
});
}
canvas.EffectiveViewportChanged += (s, e) => [Fact]
public async Task Viewport_Extends_Beyond_Nested_Centered_Control()
{
await RunOnUIThread.Execute(async () =>
{ {
Assert.Equal(new Rect(-574, -424, 1200, 900), e.EffectiveViewport); var root = CreateRoot();
++raised; var target = new Canvas { Width = 52, Height = 52 };
}; var parent = new Border { Width = 100, Height = 100, Child = target };
var raised = 0;
root.Child = outer; target.EffectiveViewportChanged += (s, e) =>
root.LayoutManager.ExecuteInitialLayoutPass(root); {
Assert.Equal(new Rect(-574, -424, 1200, 900), e.EffectiveViewport);
++raised;
};
root.Child = parent;
Assert.Equal(1, raised); await ExecuteInitialLayoutPass(root);
Assert.Equal(1, raised);
});
} }
[Fact] [Fact]
public void ScrollViewer_Determines_EffectiveViewport() public async Task ScrollViewer_Determines_EffectiveViewport()
{ {
var root = new TestRoot await RunOnUIThread.Execute(async () =>
{ {
Width = 1200, var root = CreateRoot();
Height = 900, var target = new Canvas { Width = 200, Height = 200 };
}; var scroller = new ScrollViewer { Width = 100, Height = 100, Content = target, Template = ScrollViewerTemplate() };
var raised = 0;
var canvas = new Canvas target.EffectiveViewportChanged += (s, e) =>
{ {
Width = 200, Assert.Equal(new Rect(0, 0, 100, 100), e.EffectiveViewport);
Height = 200, ++raised;
}; };
var outer = new ScrollViewer root.Child = scroller;
{
Width = 100,
Height = 100,
Content = canvas,
Template = ScrollViewerTemplate(),
};
var raised = 0; await ExecuteInitialLayoutPass(root);
Assert.Equal(1, raised);
});
}
canvas.EffectiveViewportChanged += (s, e) => [Fact]
public async Task Scrolled_ScrollViewer_Determines_EffectiveViewport()
{
await RunOnUIThread.Execute(async () =>
{ {
Assert.Equal(new Rect(0, 0, 100, 100), e.EffectiveViewport); var root = CreateRoot();
++raised; var target = new Canvas { Width = 200, Height = 200 };
}; var scroller = new ScrollViewer { Width = 100, Height = 100, Content = target, Template = ScrollViewerTemplate() };
var raised = 0;
root.Child = outer; root.Child = scroller;
root.LayoutManager.ExecuteInitialLayoutPass(root);
await ExecuteInitialLayoutPass(root);
scroller.Offset = new Vector(0, 10);
Assert.Equal(1, raised); await ExecuteScrollerLayoutPass(root, scroller, target, (s, e) =>
{
Assert.Equal(new Rect(0, 10, 100, 100), e.EffectiveViewport);
++raised;
});
Assert.Equal(1, raised);
});
} }
[Fact] [Fact]
public void Scrolled_ScrollViewer_Determines_EffectiveViewport() public async Task Moving_Parent_Updates_EffectiveViewport()
{ {
var root = new TestRoot await RunOnUIThread.Execute(async () =>
{ {
Width = 1200, var root = CreateRoot();
Height = 900, var target = new Canvas { Width = 100, Height = 100 };
}; var parent = new Border { Width = 200, Height = 200, Child = target };
var raised = 0;
var canvas = new Canvas root.Child = parent;
{
Width = 200,
Height = 200,
};
var outer = new ScrollViewer await ExecuteInitialLayoutPass(root);
{
Width = 100,
Height = 100,
Content = canvas,
Template = ScrollViewerTemplate(),
};
var raised = 0; target.EffectiveViewportChanged += (s, e) =>
{
Assert.Equal(new Rect(-554, -400, 1200, 900), e.EffectiveViewport);
++raised;
};
root.Child = outer; parent.Margin = new Thickness(8, 0, 0, 0);
root.LayoutManager.ExecuteInitialLayoutPass(root); await ExecuteLayoutPass(root);
canvas.EffectiveViewportChanged += (s, e) => Assert.Equal(1, raised);
});
}
[Fact]
public async Task Translate_Transform_Doesnt_Affect_EffectiveViewport()
{
await RunOnUIThread.Execute(async () =>
{ {
Assert.Equal(new Rect(0, 10, 100, 100), e.EffectiveViewport); var root = CreateRoot();
++raised; var target = new Canvas { Width = 100, Height = 100 };
}; var parent = new Border { Width = 200, Height = 200, Child = target };
var raised = 0;
outer.Offset = new Vector(0, 10);
root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(1, raised); root.Child = parent;
await ExecuteInitialLayoutPass(root);
target.EffectiveViewportChanged += (s, e) => ++raised;
target.RenderTransform = new TranslateTransform { X = 8 };
target.InvalidateMeasure();
await ExecuteLayoutPass(root);
Assert.Equal(0, raised);
});
} }
[Fact] [Fact]
public void Moving_Parent_Updates_EffectiveViewport() public async Task Translate_Transform_On_Parent_Affects_EffectiveViewport()
{ {
var root = new TestRoot await RunOnUIThread.Execute(async () =>
{ {
Width = 1200, var root = CreateRoot();
Height = 900, var target = new Canvas { Width = 100, Height = 100 };
}; var parent = new Border { Width = 200, Height = 200, Child = target };
var raised = 0;
var canvas = new Canvas root.Child = parent;
{
Width = 100,
Height = 100,
};
var outer = new Border await ExecuteInitialLayoutPass(root);
{
Width = 200,
Height = 200,
Child = canvas,
};
var raised = 0; target.EffectiveViewportChanged += (s, e) =>
{
Assert.Equal(new Rect(-558, -400, 1200, 900), e.EffectiveViewport);
++raised;
};
// Change the parent render transform to move it. A layout is then needed before
// EffectiveViewportChanged is raised.
parent.RenderTransform = new TranslateTransform { X = 8 };
parent.InvalidateMeasure();
await ExecuteLayoutPass(root);
Assert.Equal(1, raised);
});
}
root.Child = outer; private TestRoot CreateRoot() => new TestRoot { Width = 1200, Height = 900 };
private Task ExecuteInitialLayoutPass(TestRoot root)
{
root.LayoutManager.ExecuteInitialLayoutPass(root); root.LayoutManager.ExecuteInitialLayoutPass(root);
return Task.CompletedTask;
}
private Task ExecuteLayoutPass(TestRoot root)
{
root.LayoutManager.ExecuteLayoutPass();
return Task.CompletedTask;
}
canvas.EffectiveViewportChanged += (s, e) => private Task ExecuteScrollerLayoutPass(
TestRoot root,
ScrollViewer scroller,
Control target,
Action<object, EffectiveViewportChangedEventArgs> handler)
{
void ViewportChanged(object sender, EffectiveViewportChangedEventArgs e)
{ {
Assert.Equal(new Rect(-554, -400, 1200, 900), e.EffectiveViewport); handler(sender, e);
++raised; }
};
// Change the parent margin to move it. target.EffectiveViewportChanged += ViewportChanged;
outer.Margin = new Thickness(8, 0, 0, 0);
root.LayoutManager.ExecuteLayoutPass(); root.LayoutManager.ExecuteLayoutPass();
return Task.CompletedTask;
Assert.Equal(1, raised);
} }
private IControlTemplate ScrollViewerTemplate() private IControlTemplate ScrollViewerTemplate()
{ {
return new FuncControlTemplate<ScrollViewer>((control, scope) => new Grid return new FuncControlTemplate<ScrollViewer>((control, scope) => new Grid
@ -329,5 +377,13 @@ namespace Avalonia.Layout.UnitTests
return base.ArrangeOverride(finalSize); return base.ArrangeOverride(finalSize);
} }
} }
private static class RunOnUIThread
{
public static async Task Execute(Func<Task> func)
{
await func();
}
}
} }
} }

Loading…
Cancel
Save