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