From eb6bfd3de86dc8e5c909c5eb51c86e998f7c76c4 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 6 Jun 2017 00:46:47 +0300 Subject: [PATCH 01/15] Moved layout manager from service locator to ILayoutRoot --- src/Avalonia.Controls/Application.cs | 1 - .../Embedding/EmbeddableControlRoot.cs | 2 +- .../Presenters/ItemVirtualizerSimple.cs | 2 +- src/Avalonia.Controls/TopLevel.cs | 15 +++++++++- src/Avalonia.Controls/Window.cs | 4 +-- src/Avalonia.Controls/WindowBase.cs | 4 +-- src/Avalonia.Layout/ILayoutRoot.cs | 5 ++++ src/Avalonia.Layout/LayoutManager.cs | 5 ---- src/Avalonia.Layout/Layoutable.cs | 13 +++++++-- .../ItemsPresenterTests_Virtualization.cs | 12 ++++++-- ...emsPresenterTests_Virtualization_Simple.cs | 10 +++---- .../Primitives/PopupTests.cs | 1 - .../TopLevelTests.cs | 17 +++++++---- .../WindowBaseTests.cs | 2 +- .../FullLayoutTests.cs | 7 ++--- .../LayoutManagerTests.cs | 11 +++----- .../TestLayoutRoot.cs | 19 ++++++++++++- tests/Avalonia.LeakTests/ControlTests.cs | 28 +++++++++---------- tests/Avalonia.UnitTests/TestRoot.cs | 2 +- tests/Avalonia.UnitTests/TestServices.cs | 11 +------- tests/Avalonia.UnitTests/TestTemplatedRoot.cs | 2 +- .../Avalonia.UnitTests/UnitTestApplication.cs | 1 - 22 files changed, 104 insertions(+), 70 deletions(-) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 3d13608226..8122af36ce 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -175,7 +175,6 @@ namespace Avalonia .Bind().ToConstant(InputManager) .Bind().ToTransient() .Bind().ToConstant(_styler) - .Bind().ToSingleton() .Bind().ToConstant(this) .Bind().ToConstant(AvaloniaScheduler.Instance); } diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index b8d54fa67b..d06172b4b3 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -26,7 +26,7 @@ namespace Avalonia.Controls.Embedding { EnsureInitialized(); ApplyTemplate(); - LayoutManager.Instance.ExecuteInitialLayoutPass(this); + LayoutManager.ExecuteInitialLayoutPass(this); } private void EnsureInitialized() diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 20602d5475..38e4d2c5cc 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -510,7 +510,7 @@ namespace Avalonia.Controls.Presenters } var container = generator.ContainerFromIndex(index); - var layoutManager = LayoutManager.Instance; + var layoutManager = (Owner.GetVisualRoot() as ILayoutRoot)?.LayoutManager; // We need to do a layout here because it's possible that the container we moved to // is only partially visible due to differing item sizes. If the container is only diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index a0a8f6b27e..516b6f7e7d 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -46,6 +46,7 @@ namespace Avalonia.Controls private readonly IApplicationLifecycle _applicationLifecycle; private readonly IPlatformRenderInterface _renderInterface; private Size _clientSize; + private ILayoutManager _layoutManager; /// /// Initializes static members of the class. @@ -133,6 +134,18 @@ namespace Avalonia.Controls protected set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); } } + protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(); + + public ILayoutManager LayoutManager + { + get + { + if (_layoutManager == null) + _layoutManager = CreateLayoutManager(); + return _layoutManager; + } + } + /// /// Gets the platform-specific window implementation. /// @@ -245,7 +258,7 @@ namespace Avalonia.Controls ClientSize = clientSize; Width = clientSize.Width; Height = clientSize.Height; - LayoutManager.Instance.ExecuteLayoutPass(); + LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 3802f2b6ea..3421bd3f32 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -247,7 +247,7 @@ namespace Avalonia.Controls EnsureInitialized(); IsVisible = true; - LayoutManager.Instance.ExecuteInitialLayoutPass(this); + LayoutManager.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) { @@ -286,7 +286,7 @@ namespace Avalonia.Controls EnsureInitialized(); IsVisible = true; - LayoutManager.Instance.ExecuteInitialLayoutPass(this); + LayoutManager.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) { diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 1f484fd6cb..990d4d201c 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -136,7 +136,7 @@ namespace Avalonia.Controls { EnsureInitialized(); IsVisible = true; - LayoutManager.Instance.ExecuteInitialLayoutPass(this); + LayoutManager.ExecuteInitialLayoutPass(this); PlatformImpl?.Show(); } finally @@ -215,7 +215,7 @@ namespace Avalonia.Controls Height = clientSize.Height; } ClientSize = clientSize; - LayoutManager.Instance.ExecuteLayoutPass(); + LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } diff --git a/src/Avalonia.Layout/ILayoutRoot.cs b/src/Avalonia.Layout/ILayoutRoot.cs index 25a6331b38..700b6a8600 100644 --- a/src/Avalonia.Layout/ILayoutRoot.cs +++ b/src/Avalonia.Layout/ILayoutRoot.cs @@ -22,5 +22,10 @@ namespace Avalonia.Layout /// The scaling factor to use in layout. /// double LayoutScaling { get; } + + /// + /// Associated instance of layout manager + /// + ILayoutManager LayoutManager { get; } } } diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index b7b83bf852..ebf6411beb 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -19,11 +19,6 @@ namespace Avalonia.Layout private bool _queued; private bool _running; - /// - /// Gets the layout manager. - /// - public static ILayoutManager Instance => AvaloniaLocator.Current.GetService(); - /// public void InvalidateMeasure(ILayoutable control) { diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 20050058bf..bea62efe50 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -378,7 +378,7 @@ namespace Avalonia.Layout IsMeasureValid = false; IsArrangeValid = false; - LayoutManager.Instance?.InvalidateMeasure(this); + (VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateMeasure(this); InvalidateVisual(); } } @@ -393,7 +393,7 @@ namespace Avalonia.Layout Logger.Verbose(LogArea.Layout, this, "Invalidated arrange"); IsArrangeValid = false; - LayoutManager.Instance?.InvalidateArrange(this); + (VisualRoot as ILayoutRoot)?.LayoutManager?.InvalidateArrange(this); InvalidateVisual(); } } @@ -620,6 +620,15 @@ namespace Avalonia.Layout base.OnVisualParentChanged(oldParent, newParent); } + protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTreeCore(e); + if(!IsMeasureValid) + (VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateMeasure(this); + else if (!IsArrangeValid) + (VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateArrange(this); + } + /// /// Calls on the control on which a property changed. /// diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs index 1ea64b915c..cb0fc705ce 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs @@ -12,6 +12,7 @@ using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.UnitTests; +using Avalonia.VisualTree; using Xunit; namespace Avalonia.Controls.UnitTests.Presenters @@ -219,7 +220,7 @@ namespace Avalonia.Controls.UnitTests.Presenters [Fact] public void Changing_VirtualizationMode_None_To_Simple_Should_Add_Correct_Number_Of_Controls() { - using (UnitTestApplication.Start(TestServices.RealLayoutManager)) + using (UnitTestApplication.Start(new TestServices())) { var target = CreateTarget(mode: ItemVirtualizationMode.None); var scroll = (ScrollContentPresenter)target.Parent; @@ -237,7 +238,7 @@ namespace Avalonia.Controls.UnitTests.Presenters }; target.VirtualizationMode = ItemVirtualizationMode.Simple; - LayoutManager.Instance.ExecuteLayoutPass(); + ((ILayoutRoot)scroll.GetVisualRoot()).LayoutManager.ExecuteLayoutPass(); Assert.Equal(10, target.Panel.Children.Count); } @@ -313,11 +314,16 @@ namespace Avalonia.Controls.UnitTests.Presenters }); } - private class TestScroller : ScrollContentPresenter, IRenderRoot + private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot { public IRenderer Renderer { get; } public Size ClientSize { get; } + public Size MaxClientSize => Size.Infinity; + + public double LayoutScaling => 1; + + public ILayoutManager LayoutManager { get; } = new LayoutManager(); public IRenderTarget CreateRenderTarget() { throw new NotImplementedException(); diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs index 1da9cfce76..b18729160b 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs @@ -564,11 +564,10 @@ namespace Avalonia.Controls.UnitTests.Presenters [Fact] public void Scrolling_To_Item_In_Zero_Sized_Presenter_Doesnt_Throw() { - using (UnitTestApplication.Start(TestServices.RealLayoutManager)) + using (UnitTestApplication.Start(new TestServices())) { var target = CreateTarget(itemCount: 10); var items = (IList)target.Items; - target.ApplyTemplate(); target.Measure(Size.Empty); target.Arrange(Rect.Empty); @@ -757,7 +756,7 @@ namespace Avalonia.Controls.UnitTests.Presenters [Fact] public void GetControlInDirection_Down_Should_Scroll_If_Partially_Visible() { - using (UnitTestApplication.Start(TestServices.RealLayoutManager)) + using (UnitTestApplication.Start(new TestServices())) { var target = CreateTarget(); var scroller = (ScrollContentPresenter)target.Parent; @@ -778,7 +777,7 @@ namespace Avalonia.Controls.UnitTests.Presenters [Fact] public void GetControlInDirection_Up_Should_Scroll_If_Partially_Visible_Item_Is_Currently_Shown() { - using (UnitTestApplication.Start(TestServices.RealLayoutManager)) + using (UnitTestApplication.Start(new TestServices())) { var target = CreateTarget(); var scroller = (ScrollContentPresenter)target.Parent; @@ -869,7 +868,7 @@ namespace Avalonia.Controls.UnitTests.Presenters [Fact] public void GetControlInDirection_Right_Should_Scroll_If_Partially_Visible() { - using (UnitTestApplication.Start(TestServices.RealLayoutManager)) + using (UnitTestApplication.Start(new TestServices())) { var target = CreateTarget(orientation: Orientation.Horizontal); var scroller = (ScrollContentPresenter)target.Parent; @@ -1008,6 +1007,7 @@ namespace Avalonia.Controls.UnitTests.Presenters }; scroller.UpdateChild(); + new TestRoot().Child = scroller; return result; } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index f192e87f08..6696258258 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -270,7 +270,6 @@ namespace Avalonia.Controls.UnitTests.Primitives var renderInterface = new Mock(); AvaloniaLocator.CurrentMutable - .Bind().ToTransient() .Bind().ToFunc(() => globalStyles.Object) .Bind().ToConstant(new WindowingPlatformMock()) .Bind().ToTransient() diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index 5cd3c57e2e..35b0c75a08 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -65,15 +65,16 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Layout_Pass_Should_Not_Be_Automatically_Scheduled() { - var services = TestServices.StyledWindow.With(layoutManager: Mock.Of()); + var services = TestServices.StyledWindow; using (UnitTestApplication.Start(services)) { var impl = new Mock(); - var target = new TestTopLevel(impl.Object); + + var target = new TestTopLevel(impl.Object, Mock.Of()); // The layout pass should be scheduled by the derived class. - var layoutManagerMock = Mock.Get(LayoutManager.Instance); + var layoutManagerMock = Mock.Get(target.LayoutManager); layoutManagerMock.Verify(x => x.ExecuteLayoutPass(), Times.Never); } } @@ -98,7 +99,7 @@ namespace Avalonia.Controls.UnitTests } }; - LayoutManager.Instance.ExecuteInitialLayoutPass(target); + target.LayoutManager.ExecuteInitialLayoutPass(target); Assert.Equal(new Rect(0, 0, 321, 432), target.Bounds); } @@ -113,7 +114,7 @@ namespace Avalonia.Controls.UnitTests impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); - LayoutManager.Instance.ExecuteLayoutPass(); + target.LayoutManager.ExecuteLayoutPass(); Assert.Equal(double.NaN, target.Width); Assert.Equal(double.NaN, target.Height); @@ -222,13 +223,17 @@ namespace Avalonia.Controls.UnitTests private class TestTopLevel : TopLevel { + private readonly ILayoutManager _layoutManager; public bool IsClosed { get; private set; } - public TestTopLevel(ITopLevelImpl impl) + public TestTopLevel(ITopLevelImpl impl, ILayoutManager layoutManager = null) : base(impl) { + _layoutManager = layoutManager ?? new LayoutManager(); } + protected override ILayoutManager CreateLayoutManager() => _layoutManager; + protected override void HandleApplicationExiting() { base.HandleApplicationExiting(); diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs index d6ffb32d1c..3c3070db22 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs @@ -38,7 +38,7 @@ namespace Avalonia.Controls.UnitTests IsVisible = true, }; - LayoutManager.Instance.ExecuteInitialLayoutPass(target); + target.LayoutManager.ExecuteInitialLayoutPass(target); Mock.Get(impl).Verify(x => x.Resize(new Size(321, 432))); } diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs index 6b7c73da2a..7d79d369cb 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs @@ -56,11 +56,11 @@ namespace Avalonia.Layout.UnitTests }; window.Show(); - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.Equal(new Size(400, 400), border.Bounds.Size); textBlock.Width = 200; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Equal(new Size(200, 400), border.Bounds.Size); } @@ -98,7 +98,7 @@ namespace Avalonia.Layout.UnitTests }; window.Show(); - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.Equal(new Size(800, 600), window.Bounds.Size); Assert.Equal(new Size(200, 200), scrollViewer.Bounds.Size); @@ -186,7 +186,6 @@ namespace Avalonia.Layout.UnitTests .Bind().ToConstant(new AssetLoader()) .Bind().ToConstant(new Mock().Object) .Bind().ToConstant(globalStyles.Object) - .Bind().ToConstant(new LayoutManager()) .Bind().ToConstant(new AppBuilder().RuntimePlatform) .Bind().ToConstant(renderInterface.Object) .Bind().ToConstant(new Styler()) diff --git a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs index f67c5a353f..d76c9ea6bc 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs @@ -11,12 +11,9 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Invalidating_Child_Should_Remeasure_Parent() { - var layoutManager = new LayoutManager(); - using (AvaloniaLocator.EnterScope()) { - AvaloniaLocator.CurrentMutable.Bind().ToConstant(layoutManager); - + Border border; StackPanel panel; @@ -30,14 +27,14 @@ namespace Avalonia.Layout.UnitTests } } }; - - layoutManager.ExecuteInitialLayoutPass(root); + + root.LayoutManager.ExecuteInitialLayoutPass(root); Assert.Equal(new Size(0, 0), root.DesiredSize); border.Width = 100; border.Height = 100; - layoutManager.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.Equal(new Size(100, 100), panel.DesiredSize); } } diff --git a/tests/Avalonia.Layout.UnitTests/TestLayoutRoot.cs b/tests/Avalonia.Layout.UnitTests/TestLayoutRoot.cs index fab1647c5d..8c4dfbb209 100644 --- a/tests/Avalonia.Layout.UnitTests/TestLayoutRoot.cs +++ b/tests/Avalonia.Layout.UnitTests/TestLayoutRoot.cs @@ -2,10 +2,12 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using Avalonia.Controls; +using Avalonia.Platform; +using Avalonia.Rendering; namespace Avalonia.Layout.UnitTests { - internal class TestLayoutRoot : Decorator, ILayoutRoot + internal class TestLayoutRoot : Decorator, ILayoutRoot, IRenderRoot { public TestLayoutRoot() { @@ -18,7 +20,22 @@ namespace Avalonia.Layout.UnitTests set; } + public IRenderer Renderer => null; + + public IRenderTarget CreateRenderTarget() => null; + + public void Invalidate(Rect rect) + { + } + + public Point PointToClient(Point point) => point; + + public Point PointToScreen(Point point) => point; + public Size MaxClientSize => Size.Infinity; public double LayoutScaling => 1; + + public ILayoutManager LayoutManager { get; set; } = new LayoutManager(); + } } diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index b9bdee09e6..fa004976c0 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -42,12 +42,12 @@ namespace Avalonia.LeakTests window.Show(); // Do a layout and make sure that Canvas gets added to visual tree. - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.IsType(window.Presenter.Child); // Clear the content and ensure the Canvas is removed. window.Content = null; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Null(window.Presenter.Child); return window; @@ -78,13 +78,13 @@ namespace Avalonia.LeakTests window.Show(); // Do a layout and make sure that Canvas gets added to visual tree. - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.IsType(window.Find("foo")); Assert.IsType(window.Presenter.Child); // Clear the content and ensure the Canvas is removed. window.Content = null; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Null(window.Presenter.Child); return window; @@ -116,13 +116,13 @@ namespace Avalonia.LeakTests // Do a layout and make sure that ScrollViewer gets added to visual tree and its // template applied. - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.IsType(window.Presenter.Child); Assert.IsType(((ScrollViewer)window.Presenter.Child).Presenter.Child); // Clear the content and ensure the ScrollViewer is removed. window.Content = null; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Null(window.Presenter.Child); return window; @@ -153,13 +153,13 @@ namespace Avalonia.LeakTests // Do a layout and make sure that TextBox gets added to visual tree and its // template applied. - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.IsType(window.Presenter.Child); Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count()); // Clear the content and ensure the TextBox is removed. window.Content = null; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Null(window.Presenter.Child); return window; @@ -197,14 +197,14 @@ namespace Avalonia.LeakTests // Do a layout and make sure that TextBox gets added to visual tree and its // Text property set. - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.IsType(window.Presenter.Child); Assert.Equal("foo", ((TextBox)window.Presenter.Child).Text); // Clear the content and DataContext and ensure the TextBox is removed. window.Content = null; window.DataContext = null; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Null(window.Presenter.Child); return window; @@ -235,7 +235,7 @@ namespace Avalonia.LeakTests // Do a layout and make sure that TextBox gets added to visual tree and its // template applied. - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.Same(textBox, window.Presenter.Child); // Get the border from the TextBox template. @@ -247,7 +247,7 @@ namespace Avalonia.LeakTests // Clear the content and ensure the TextBox is removed. window.Content = null; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Null(window.Presenter.Child); // Check that the TextBox has no subscriptions to its Classes collection. @@ -289,12 +289,12 @@ namespace Avalonia.LeakTests window.Show(); // Do a layout and make sure that TreeViewItems get realized. - LayoutManager.Instance.ExecuteInitialLayoutPass(window); + window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.Equal(1, target.ItemContainerGenerator.Containers.Count()); // Clear the content and ensure the TreeView is removed. window.Content = null; - LayoutManager.Instance.ExecuteLayoutPass(); + window.LayoutManager.ExecuteLayoutPass(); Assert.Null(window.Presenter.Child); return window; diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs index 8a711c415e..7387602213 100644 --- a/tests/Avalonia.UnitTests/TestRoot.cs +++ b/tests/Avalonia.UnitTests/TestRoot.cs @@ -47,7 +47,7 @@ namespace Avalonia.UnitTests public double LayoutScaling => 1; - public ILayoutManager LayoutManager => AvaloniaLocator.Current.GetService(); + public ILayoutManager LayoutManager { get; set; } = new LayoutManager(); public IRenderTarget RenderTarget => null; diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs index 0cd8d4295b..6699c33be9 100644 --- a/tests/Avalonia.UnitTests/TestServices.cs +++ b/tests/Avalonia.UnitTests/TestServices.cs @@ -21,7 +21,6 @@ namespace Avalonia.UnitTests { public static readonly TestServices StyledWindow = new TestServices( assetLoader: new AssetLoader(), - layoutManager: new LayoutManager(), platform: new AppBuilder().RuntimePlatform, renderer: (_, __) => Mock.Of(), renderInterface: CreateRenderInterfaceMock(), @@ -51,10 +50,7 @@ namespace Avalonia.UnitTests focusManager: new FocusManager(), keyboardDevice: () => new KeyboardDevice(), inputManager: new InputManager()); - - public static readonly TestServices RealLayoutManager = new TestServices( - layoutManager: new LayoutManager()); - + public static readonly TestServices RealStyler = new TestServices( styler: new Styler()); @@ -63,7 +59,6 @@ namespace Avalonia.UnitTests IFocusManager focusManager = null, IInputManager inputManager = null, Func keyboardDevice = null, - ILayoutManager layoutManager = null, IRuntimePlatform platform = null, Func renderer = null, IPlatformRenderInterface renderInterface = null, @@ -79,7 +74,6 @@ namespace Avalonia.UnitTests FocusManager = focusManager; InputManager = inputManager; KeyboardDevice = keyboardDevice; - LayoutManager = layoutManager; Platform = platform; Renderer = renderer; RenderInterface = renderInterface; @@ -96,7 +90,6 @@ namespace Avalonia.UnitTests public IInputManager InputManager { get; } public IFocusManager FocusManager { get; } public Func KeyboardDevice { get; } - public ILayoutManager LayoutManager { get; } public IRuntimePlatform Platform { get; } public Func Renderer { get; } public IPlatformRenderInterface RenderInterface { get; } @@ -113,7 +106,6 @@ namespace Avalonia.UnitTests IFocusManager focusManager = null, IInputManager inputManager = null, Func keyboardDevice = null, - ILayoutManager layoutManager = null, IRuntimePlatform platform = null, Func renderer = null, IPlatformRenderInterface renderInterface = null, @@ -131,7 +123,6 @@ namespace Avalonia.UnitTests focusManager: focusManager ?? FocusManager, inputManager: inputManager ?? InputManager, keyboardDevice: keyboardDevice ?? KeyboardDevice, - layoutManager: layoutManager ?? LayoutManager, platform: platform ?? Platform, renderer: renderer ?? Renderer, renderInterface: renderInterface ?? RenderInterface, diff --git a/tests/Avalonia.UnitTests/TestTemplatedRoot.cs b/tests/Avalonia.UnitTests/TestTemplatedRoot.cs index 73c9f370d6..8bed09dd27 100644 --- a/tests/Avalonia.UnitTests/TestTemplatedRoot.cs +++ b/tests/Avalonia.UnitTests/TestTemplatedRoot.cs @@ -39,7 +39,7 @@ namespace Avalonia.UnitTests public double LayoutScaling => 1; - public ILayoutManager LayoutManager => AvaloniaLocator.Current.GetService(); + public ILayoutManager LayoutManager { get; set; } = new LayoutManager(); public IRenderTarget RenderTarget => null; diff --git a/tests/Avalonia.UnitTests/UnitTestApplication.cs b/tests/Avalonia.UnitTests/UnitTestApplication.cs index c5d533486b..22d0901a14 100644 --- a/tests/Avalonia.UnitTests/UnitTestApplication.cs +++ b/tests/Avalonia.UnitTests/UnitTestApplication.cs @@ -49,7 +49,6 @@ namespace Avalonia.UnitTests .BindToSelf(this) .Bind().ToConstant(Services.InputManager) .Bind().ToConstant(Services.KeyboardDevice?.Invoke()) - .Bind().ToConstant(Services.LayoutManager) .Bind().ToConstant(Services.Platform) .Bind().ToConstant(new RendererFactory(Services.Renderer)) .Bind().ToConstant(Services.RenderInterface) From 00fbc5cea70b6925b1fa121f3868cc1d300f6b2d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 6 Jun 2017 01:30:21 +0300 Subject: [PATCH 02/15] Use (0, 0) as default for non-existing PreviousMeasure --- src/Avalonia.Layout/LayoutManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index ebf6411beb..ed2b930114 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -133,7 +133,7 @@ namespace Avalonia.Layout if (!control.IsMeasureValid) { - control.Measure(control.PreviousMeasure.Value); + control.Measure(control.PreviousMeasure ?? default(Size)); } _toMeasure.Remove(control); From 48df92055ec66285e04683b22034a748d549fe43 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 11 Jun 2017 13:36:06 +0200 Subject: [PATCH 03/15] Fix ItemsPresenterSimple tests. There were two problems: - There were two root controls (`TestScroller` and `TestRoot`) - The root control needs to have a fixed size otherwise it will grow because `LayoutManager` passes `MaxClientSize` to its measure (which may be different to the initial measure that we were using to set its size). --- ...emsPresenterTests_Virtualization_Simple.cs | 121 +++++++++--------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs index b18729160b..7a7f4ab4ec 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs @@ -12,6 +12,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Input; +using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.UnitTests; @@ -722,10 +723,10 @@ namespace Avalonia.Controls.UnitTests.Presenters public void GetControlInDirection_Down_Should_Return_Existing_Container_If_Materialized() { var target = CreateTarget(); + var scroller = (TestScroller)target.Parent; - target.ApplyTemplate(); - target.Measure(new Size(100, 100)); - target.Arrange(new Rect(0, 0, 100, 100)); + scroller.Width = scroller.Height = 100; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); var from = target.Panel.Children[5]; var result = ((ILogicalScrollable)target).GetControlInDirection( @@ -739,10 +740,10 @@ namespace Avalonia.Controls.UnitTests.Presenters public void GetControlInDirection_Down_Should_Scroll_If_Necessary() { var target = CreateTarget(); + var scroller = (TestScroller)target.Parent; - target.ApplyTemplate(); - target.Measure(new Size(100, 100)); - target.Arrange(new Rect(0, 0, 100, 100)); + scroller.Width = scroller.Height = 100; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); var from = target.Panel.Children[9]; var result = ((ILogicalScrollable)target).GetControlInDirection( @@ -756,44 +757,40 @@ namespace Avalonia.Controls.UnitTests.Presenters [Fact] public void GetControlInDirection_Down_Should_Scroll_If_Partially_Visible() { - using (UnitTestApplication.Start(new TestServices())) - { - var target = CreateTarget(); - var scroller = (ScrollContentPresenter)target.Parent; + var target = CreateTarget(); + var scroller = (TestScroller)target.Parent; - scroller.Measure(new Size(100, 95)); - scroller.Arrange(new Rect(0, 0, 100, 95)); + scroller.Width = 100; + scroller.Height = 95; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); - var from = target.Panel.Children[8]; - var result = ((ILogicalScrollable)target).GetControlInDirection( - NavigationDirection.Down, - from); + var from = target.Panel.Children[8]; + var result = ((ILogicalScrollable)target).GetControlInDirection( + NavigationDirection.Down, + from); - Assert.Equal(new Vector(0, 1), ((ILogicalScrollable)target).Offset); - Assert.Same(target.Panel.Children[8], result); - } + Assert.Equal(new Vector(0, 1), ((ILogicalScrollable)target).Offset); + Assert.Same(target.Panel.Children[8], result); } [Fact] public void GetControlInDirection_Up_Should_Scroll_If_Partially_Visible_Item_Is_Currently_Shown() { - using (UnitTestApplication.Start(new TestServices())) - { - var target = CreateTarget(); - var scroller = (ScrollContentPresenter)target.Parent; + var target = CreateTarget(); + var scroller = (TestScroller)target.Parent; - scroller.Measure(new Size(100, 95)); - scroller.Arrange(new Rect(0, 0, 100, 95)); - ((ILogicalScrollable)target).Offset = new Vector(0, 11); + scroller.Width = 100; + scroller.Height = 95; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); + ((ILogicalScrollable)target).Offset = new Vector(0, 11); - var from = target.Panel.Children[1]; - var result = ((ILogicalScrollable)target).GetControlInDirection( - NavigationDirection.Up, - from); + var from = target.Panel.Children[1]; + var result = ((ILogicalScrollable)target).GetControlInDirection( + NavigationDirection.Up, + from); - Assert.Equal(new Vector(0, 10), ((ILogicalScrollable)target).Offset); - Assert.Same(target.Panel.Children[0], result); - } + Assert.Equal(new Vector(0, 10), ((ILogicalScrollable)target).Offset); + Assert.Same(target.Panel.Children[0], result); } [Fact] @@ -834,10 +831,10 @@ namespace Avalonia.Controls.UnitTests.Presenters public void GetControlInDirection_Right_Should_Return_Existing_Container_If_Materialized() { var target = CreateTarget(orientation: Orientation.Horizontal); + var scroller = (TestScroller)target.Parent; - target.ApplyTemplate(); - target.Measure(new Size(100, 100)); - target.Arrange(new Rect(0, 0, 100, 100)); + scroller.Width = scroller.Height = 100; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); var from = target.Panel.Children[5]; var result = ((ILogicalScrollable)target).GetControlInDirection( @@ -851,10 +848,10 @@ namespace Avalonia.Controls.UnitTests.Presenters public void GetControlInDirection_Right_Should_Scroll_If_Necessary() { var target = CreateTarget(orientation: Orientation.Horizontal); + var scroller = (TestScroller)target.Parent; - target.ApplyTemplate(); - target.Measure(new Size(100, 100)); - target.Arrange(new Rect(0, 0, 100, 100)); + scroller.Width = scroller.Height = 100; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); var from = target.Panel.Children[9]; var result = ((ILogicalScrollable)target).GetControlInDirection( @@ -868,32 +865,31 @@ namespace Avalonia.Controls.UnitTests.Presenters [Fact] public void GetControlInDirection_Right_Should_Scroll_If_Partially_Visible() { - using (UnitTestApplication.Start(new TestServices())) - { - var target = CreateTarget(orientation: Orientation.Horizontal); - var scroller = (ScrollContentPresenter)target.Parent; + var target = CreateTarget(orientation: Orientation.Horizontal); + var scroller = (TestScroller)target.Parent; - scroller.Measure(new Size(95, 100)); - scroller.Arrange(new Rect(0, 0, 95, 100)); + scroller.Width = 95; + scroller.Height = 100; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); - var from = target.Panel.Children[8]; - var result = ((ILogicalScrollable)target).GetControlInDirection( - NavigationDirection.Right, - from); + var from = target.Panel.Children[8]; + var result = ((ILogicalScrollable)target).GetControlInDirection( + NavigationDirection.Right, + from); - Assert.Equal(new Vector(1, 0), ((ILogicalScrollable)target).Offset); - Assert.Same(target.Panel.Children[8], result); - } + Assert.Equal(new Vector(1, 0), ((ILogicalScrollable)target).Offset); + Assert.Same(target.Panel.Children[8], result); } [Fact] public void GetControlInDirection_Left_Should_Scroll_If_Partially_Visible_Item_Is_Currently_Shown() { var target = CreateTarget(orientation: Orientation.Horizontal); + var scroller = (TestScroller)target.Parent; - target.ApplyTemplate(); - target.Measure(new Size(95, 100)); - target.Arrange(new Rect(0, 0, 95, 100)); + scroller.Width = 95; + scroller.Height = 100; + scroller.LayoutManager.ExecuteInitialLayoutPass(scroller); ((ILogicalScrollable)target).Offset = new Vector(11, 0); var from = target.Panel.Children[1]; @@ -1007,8 +1003,6 @@ namespace Avalonia.Controls.UnitTests.Presenters }; scroller.UpdateChild(); - new TestRoot().Child = scroller; - return result; } @@ -1030,11 +1024,17 @@ namespace Avalonia.Controls.UnitTests.Presenters }); } - private class TestScroller : ScrollContentPresenter, IRenderRoot + private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot { public IRenderer Renderer { get; } public Size ClientSize { get; } + public Size MaxClientSize => Size.Infinity; + + public double LayoutScaling => 1; + + public ILayoutManager LayoutManager { get; } = new LayoutManager(); + public IRenderTarget CreateRenderTarget() { throw new NotImplementedException(); @@ -1054,6 +1054,11 @@ namespace Avalonia.Controls.UnitTests.Presenters { throw new NotImplementedException(); } + + protected override Size MeasureOverride(Size availableSize) + { + return base.MeasureOverride(availableSize); + } } private class TestItemsPresenter : ItemsPresenter From 4b8db11f0d237fd4e1b8bfbeaac1ab399514631f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 11 Jun 2017 13:38:16 +0200 Subject: [PATCH 04/15] FIx ItemsPresenterTests_Virtualization. The root control must have a fixed size, doing an initial measure isn't enough as the `LayoutManager` will remeasure with `MaxClientSize`. --- .../Presenters/ItemsPresenterTests_Virtualization.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs index cb0fc705ce..c69eebf324 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs @@ -223,10 +223,10 @@ namespace Avalonia.Controls.UnitTests.Presenters using (UnitTestApplication.Start(new TestServices())) { var target = CreateTarget(mode: ItemVirtualizationMode.None); - var scroll = (ScrollContentPresenter)target.Parent; + var scroll = (TestScroller)target.Parent; - scroll.Measure(new Size(100, 100)); - scroll.Arrange(new Rect(0, 0, 100, 100)); + scroll.Width = scroll.Height = 100; + scroll.LayoutManager.ExecuteInitialLayoutPass(scroll); // Ensure than an intermediate measure pass doesn't add more controls than it // should. This can happen if target gets measured with Size.Infinity which From 188a0ce442d7b8fb073ebfb9c1f07b50d561c3c7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2017 01:45:49 +0200 Subject: [PATCH 05/15] Fix compile errors. --- tests/Avalonia.Benchmarks/Layout/Measure.cs | 14 +++----------- .../LayoutManagerTests.cs | 2 -- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/tests/Avalonia.Benchmarks/Layout/Measure.cs b/tests/Avalonia.Benchmarks/Layout/Measure.cs index d1fdae9971..b0490d8a0f 100644 --- a/tests/Avalonia.Benchmarks/Layout/Measure.cs +++ b/tests/Avalonia.Benchmarks/Layout/Measure.cs @@ -8,26 +8,18 @@ using BenchmarkDotNet.Attributes; namespace Avalonia.Benchmarks.Layout { [MemoryDiagnoser] - public class Measure : IDisposable + public class Measure { - private IDisposable _app; private TestRoot root; private List controls = new List(); public Measure() { - _app = UnitTestApplication.Start(TestServices.RealLayoutManager); - var panel = new StackPanel(); root = new TestRoot { Child = panel }; controls.Add(panel); CreateChildren(panel, 3, 5); - LayoutManager.Instance.ExecuteInitialLayoutPass(root); - } - - public void Dispose() - { - _app.Dispose(); + root.LayoutManager.ExecuteInitialLayoutPass(root); } [Benchmark] @@ -43,7 +35,7 @@ namespace Avalonia.Benchmarks.Layout } } - LayoutManager.Instance.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); } private void CreateChildren(IPanel parent, int childCount, int iterations) diff --git a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs index c4c0f9a441..5f43a8d00e 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs @@ -264,8 +264,6 @@ namespace Avalonia.Layout.UnitTests { using (AvaloniaLocator.EnterScope()) { - AvaloniaLocator.CurrentMutable.Bind().ToConstant(layoutManager); - Border border; StackPanel panel; From 5e3f604308e514e3392160e307602288d9f1e53a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2017 01:51:11 +0200 Subject: [PATCH 06/15] Removed unnecessary service locator code. `ILayoutManager` is not longer retrieved from `AvaloniaLocator` so can remove this stuff. --- .../LayoutManagerTests.cs | 314 ++++++++---------- .../LayoutableTests.cs | 87 +++-- 2 files changed, 174 insertions(+), 227 deletions(-) diff --git a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs index 5f43a8d00e..3526b29cb5 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs @@ -15,285 +15,239 @@ namespace Avalonia.Layout.UnitTests public void Measures_And_Arranges_InvalidateMeasured_Control() { var target = new LayoutManager(); + var control = new LayoutTestControl(); + var root = new LayoutTestRoot { Child = control }; - using (Start(target)) - { - var control = new LayoutTestControl(); - var root = new LayoutTestRoot { Child = control }; - - target.ExecuteInitialLayoutPass(root); - control.Measured = control.Arranged = false; + target.ExecuteInitialLayoutPass(root); + control.Measured = control.Arranged = false; - control.InvalidateMeasure(); - target.ExecuteLayoutPass(); + control.InvalidateMeasure(); + target.ExecuteLayoutPass(); - Assert.True(control.Measured); - Assert.True(control.Arranged); - } + Assert.True(control.Measured); + Assert.True(control.Arranged); } [Fact] public void Arranges_InvalidateArranged_Control() { var target = new LayoutManager(); + var control = new LayoutTestControl(); + var root = new LayoutTestRoot { Child = control }; - using (Start(target)) - { - var control = new LayoutTestControl(); - var root = new LayoutTestRoot { Child = control }; - - target.ExecuteInitialLayoutPass(root); - control.Measured = control.Arranged = false; + target.ExecuteInitialLayoutPass(root); + control.Measured = control.Arranged = false; - control.InvalidateArrange(); - target.ExecuteLayoutPass(); + control.InvalidateArrange(); + target.ExecuteLayoutPass(); - Assert.False(control.Measured); - Assert.True(control.Arranged); - } + Assert.False(control.Measured); + Assert.True(control.Arranged); } [Fact] public void Measures_Parent_Of_Newly_Added_Control() { var target = new LayoutManager(); + var control = new LayoutTestControl(); + var root = new LayoutTestRoot(); - using (Start(target)) - { - var control = new LayoutTestControl(); - var root = new LayoutTestRoot(); - - target.ExecuteInitialLayoutPass(root); - root.Child = control; - root.Measured = root.Arranged = false; + target.ExecuteInitialLayoutPass(root); + root.Child = control; + root.Measured = root.Arranged = false; - target.ExecuteLayoutPass(); + target.ExecuteLayoutPass(); - Assert.True(root.Measured); - Assert.True(root.Arranged); - Assert.True(control.Measured); - Assert.True(control.Arranged); - } + Assert.True(root.Measured); + Assert.True(root.Arranged); + Assert.True(control.Measured); + Assert.True(control.Arranged); } [Fact] public void Measures_In_Correct_Order() { var target = new LayoutManager(); - - using (Start(target)) + LayoutTestControl control1; + LayoutTestControl control2; + var root = new LayoutTestRoot { - LayoutTestControl control1; - LayoutTestControl control2; - var root = new LayoutTestRoot + Child = control1 = new LayoutTestControl { - Child = control1 = new LayoutTestControl - { - Child = control2 = new LayoutTestControl(), - } - }; + Child = control2 = new LayoutTestControl(), + } + }; - var order = new List(); - Size MeasureOverride(ILayoutable control, Size size) - { - order.Add(control); - return new Size(10, 10); - } + var order = new List(); + Size MeasureOverride(ILayoutable control, Size size) + { + order.Add(control); + return new Size(10, 10); + } - root.DoMeasureOverride = MeasureOverride; - control1.DoMeasureOverride = MeasureOverride; - control2.DoMeasureOverride = MeasureOverride; - target.ExecuteInitialLayoutPass(root); + root.DoMeasureOverride = MeasureOverride; + control1.DoMeasureOverride = MeasureOverride; + control2.DoMeasureOverride = MeasureOverride; + target.ExecuteInitialLayoutPass(root); - control2.InvalidateMeasure(); - control1.InvalidateMeasure(); - root.InvalidateMeasure(); + control2.InvalidateMeasure(); + control1.InvalidateMeasure(); + root.InvalidateMeasure(); - order.Clear(); - target.ExecuteLayoutPass(); + order.Clear(); + target.ExecuteLayoutPass(); - Assert.Equal(new ILayoutable[] { root, control1, control2 }, order); - } + Assert.Equal(new ILayoutable[] { root, control1, control2 }, order); } [Fact] public void Measures_Root_And_Grandparent_In_Correct_Order() { var target = new LayoutManager(); - - using (Start(target)) + LayoutTestControl control1; + LayoutTestControl control2; + var root = new LayoutTestRoot { - LayoutTestControl control1; - LayoutTestControl control2; - var root = new LayoutTestRoot + Child = control1 = new LayoutTestControl { - Child = control1 = new LayoutTestControl - { - Child = control2 = new LayoutTestControl(), - } - }; + Child = control2 = new LayoutTestControl(), + } + }; - var order = new List(); - Size MeasureOverride(ILayoutable control, Size size) - { - order.Add(control); - return new Size(10, 10); - } + var order = new List(); + Size MeasureOverride(ILayoutable control, Size size) + { + order.Add(control); + return new Size(10, 10); + } - root.DoMeasureOverride = MeasureOverride; - control1.DoMeasureOverride = MeasureOverride; - control2.DoMeasureOverride = MeasureOverride; - target.ExecuteInitialLayoutPass(root); + root.DoMeasureOverride = MeasureOverride; + control1.DoMeasureOverride = MeasureOverride; + control2.DoMeasureOverride = MeasureOverride; + target.ExecuteInitialLayoutPass(root); - control2.InvalidateMeasure(); - root.InvalidateMeasure(); + control2.InvalidateMeasure(); + root.InvalidateMeasure(); - order.Clear(); - target.ExecuteLayoutPass(); + order.Clear(); + target.ExecuteLayoutPass(); - Assert.Equal(new ILayoutable[] { root, control2 }, order); - } + Assert.Equal(new ILayoutable[] { root, control2 }, order); } [Fact] public void Doesnt_Measure_Non_Invalidated_Root() { var target = new LayoutManager(); + var control = new LayoutTestControl(); + var root = new LayoutTestRoot { Child = control }; - using (Start(target)) - { - var control = new LayoutTestControl(); - var root = new LayoutTestRoot { Child = control }; + target.ExecuteInitialLayoutPass(root); + root.Measured = root.Arranged = false; + control.Measured = control.Arranged = false; - target.ExecuteInitialLayoutPass(root); - root.Measured = root.Arranged = false; - control.Measured = control.Arranged = false; + control.InvalidateMeasure(); + target.ExecuteLayoutPass(); - control.InvalidateMeasure(); - target.ExecuteLayoutPass(); - - Assert.False(root.Measured); - Assert.False(root.Arranged); - Assert.True(control.Measured); - Assert.True(control.Arranged); - } + Assert.False(root.Measured); + Assert.False(root.Arranged); + Assert.True(control.Measured); + Assert.True(control.Arranged); } [Fact] public void Doesnt_Measure_Removed_Control() { var target = new LayoutManager(); + var control = new LayoutTestControl(); + var root = new LayoutTestRoot { Child = control }; - using (Start(target)) - { - var control = new LayoutTestControl(); - var root = new LayoutTestRoot { Child = control }; - - target.ExecuteInitialLayoutPass(root); - control.Measured = control.Arranged = false; + target.ExecuteInitialLayoutPass(root); + control.Measured = control.Arranged = false; - control.InvalidateMeasure(); - root.Child = null; - target.ExecuteLayoutPass(); + control.InvalidateMeasure(); + root.Child = null; + target.ExecuteLayoutPass(); - Assert.False(control.Measured); - Assert.False(control.Arranged); - } + Assert.False(control.Measured); + Assert.False(control.Arranged); } [Fact] public void Measures_Root_With_Infinity() { var target = new LayoutManager(); + var root = new LayoutTestRoot(); + var availableSize = default(Size); - using (Start(target)) - { - var root = new LayoutTestRoot(); - var availableSize = default(Size); - - // Should not measure with this size. - root.MaxClientSize = new Size(123, 456); + // Should not measure with this size. + root.MaxClientSize = new Size(123, 456); - root.DoMeasureOverride = (_, s) => - { - availableSize = s; - return new Size(100, 100); - }; + root.DoMeasureOverride = (_, s) => + { + availableSize = s; + return new Size(100, 100); + }; - target.ExecuteInitialLayoutPass(root); + target.ExecuteInitialLayoutPass(root); - Assert.Equal(Size.Infinity, availableSize); - } + Assert.Equal(Size.Infinity, availableSize); } [Fact] public void Arranges_Root_With_DesiredSize() { var target = new LayoutManager(); - - using (Start(target)) + var root = new LayoutTestRoot { - var root = new LayoutTestRoot - { - Width = 100, - Height = 100, - }; + Width = 100, + Height = 100, + }; - var arrangeSize = default(Size); + var arrangeSize = default(Size); - root.DoArrangeOverride = (_, s) => - { - arrangeSize = s; - return s; - }; + root.DoArrangeOverride = (_, s) => + { + arrangeSize = s; + return s; + }; - target.ExecuteInitialLayoutPass(root); - Assert.Equal(new Size(100, 100), arrangeSize); + target.ExecuteInitialLayoutPass(root); + Assert.Equal(new Size(100, 100), arrangeSize); - root.Width = 120; + root.Width = 120; - target.ExecuteLayoutPass(); - Assert.Equal(new Size(120, 100), arrangeSize); - } + target.ExecuteLayoutPass(); + Assert.Equal(new Size(120, 100), arrangeSize); } [Fact] public void Invalidating_Child_Remeasures_Parent() { - using (AvaloniaLocator.EnterScope()) - { - Border border; - StackPanel panel; + Border border; + StackPanel panel; - var root = new LayoutTestRoot + var root = new LayoutTestRoot + { + Child = panel = new StackPanel + { + Children = new Controls.Controls { - Child = panel = new StackPanel - { - Children = new Controls.Controls - { - (border = new Border()) - } - } - }; + (border = new Border()) + } + } + }; - root.LayoutManager.ExecuteInitialLayoutPass(root); - Assert.Equal(new Size(0, 0), root.DesiredSize); + root.LayoutManager.ExecuteInitialLayoutPass(root); + Assert.Equal(new Size(0, 0), root.DesiredSize); - border.Width = 100; - border.Height = 100; + border.Width = 100; + border.Height = 100; - root.LayoutManager.ExecuteLayoutPass(); - Assert.Equal(new Size(100, 100), panel.DesiredSize); - } - } - - private IDisposable Start(LayoutManager layoutManager) - { - var result = AvaloniaLocator.EnterScope(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(layoutManager); - return result; + root.LayoutManager.ExecuteLayoutPass(); + Assert.Equal(new Size(100, 100), panel.DesiredSize); } } } diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs index dcc65edc74..68cdaa1b12 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs @@ -11,80 +11,73 @@ namespace Avalonia.Layout.UnitTests public void Only_Calls_LayoutManager_InvalidateMeasure_Once() { var target = new Mock(); - - using (Start(target.Object)) + var control = new Decorator(); + var root = new LayoutTestRoot { - var control = new Decorator(); - var root = new LayoutTestRoot { Child = control }; + Child = control, + LayoutManager = target.Object, + }; - root.Measure(Size.Infinity); - root.Arrange(new Rect(root.DesiredSize)); - target.ResetCalls(); + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + target.ResetCalls(); - control.InvalidateMeasure(); - control.InvalidateMeasure(); + control.InvalidateMeasure(); + control.InvalidateMeasure(); - target.Verify(x => x.InvalidateMeasure(control), Times.Once()); - } + target.Verify(x => x.InvalidateMeasure(control), Times.Once()); } [Fact] public void Only_Calls_LayoutManager_InvalidateArrange_Once() { var target = new Mock(); - - using (Start(target.Object)) + var control = new Decorator(); + var root = new LayoutTestRoot { - var control = new Decorator(); - var root = new LayoutTestRoot { Child = control }; + Child = control, + LayoutManager = target.Object, + }; - root.Measure(Size.Infinity); - root.Arrange(new Rect(root.DesiredSize)); - target.ResetCalls(); + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + target.ResetCalls(); - control.InvalidateArrange(); - control.InvalidateArrange(); + control.InvalidateArrange(); + control.InvalidateArrange(); - target.Verify(x => x.InvalidateArrange(control), Times.Once()); - } + target.Verify(x => x.InvalidateArrange(control), Times.Once()); } [Fact] public void Attaching_Control_To_Tree_Invalidates_Parent_Measure() { var target = new Mock(); - - using (Start(target.Object)) + var control = new Decorator(); + var root = new LayoutTestRoot { - var control = new Decorator(); - var root = new LayoutTestRoot { Child = control }; + Child = control, + LayoutManager = target.Object, + }; - root.Measure(Size.Infinity); - root.Arrange(new Rect(root.DesiredSize)); - Assert.True(control.IsMeasureValid); + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + Assert.True(control.IsMeasureValid); - root.Child = null; - root.Measure(Size.Infinity); - root.Arrange(new Rect(root.DesiredSize)); + root.Child = null; + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); - Assert.False(control.IsMeasureValid); - Assert.True(root.IsMeasureValid); + Assert.False(control.IsMeasureValid); + Assert.True(root.IsMeasureValid); - target.ResetCalls(); + target.ResetCalls(); - root.Child = control; + root.Child = control; - Assert.False(root.IsMeasureValid); - Assert.False(control.IsMeasureValid); - target.Verify(x => x.InvalidateMeasure(root), Times.Once()); - } - } - - private IDisposable Start(ILayoutManager layoutManager) - { - var result = AvaloniaLocator.EnterScope(); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(layoutManager); - return result; + Assert.False(root.IsMeasureValid); + Assert.False(control.IsMeasureValid); + target.Verify(x => x.InvalidateMeasure(root), Times.Once()); } } } From 061a264ca4fc0d2ff237fb86500a7577f42b020e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2017 01:55:46 +0200 Subject: [PATCH 07/15] Don't need to create LayoutManager target. There is already one on the root. --- .../LayoutManagerTests.cs | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs index 3526b29cb5..70b5d5a991 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs @@ -14,15 +14,14 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Measures_And_Arranges_InvalidateMeasured_Control() { - var target = new LayoutManager(); var control = new LayoutTestControl(); var root = new LayoutTestRoot { Child = control }; - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); control.Measured = control.Arranged = false; control.InvalidateMeasure(); - target.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.True(control.Measured); Assert.True(control.Arranged); @@ -31,15 +30,14 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Arranges_InvalidateArranged_Control() { - var target = new LayoutManager(); var control = new LayoutTestControl(); var root = new LayoutTestRoot { Child = control }; - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); control.Measured = control.Arranged = false; control.InvalidateArrange(); - target.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.False(control.Measured); Assert.True(control.Arranged); @@ -48,15 +46,14 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Measures_Parent_Of_Newly_Added_Control() { - var target = new LayoutManager(); var control = new LayoutTestControl(); var root = new LayoutTestRoot(); - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); root.Child = control; root.Measured = root.Arranged = false; - target.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.True(root.Measured); Assert.True(root.Arranged); @@ -67,7 +64,6 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Measures_In_Correct_Order() { - var target = new LayoutManager(); LayoutTestControl control1; LayoutTestControl control2; var root = new LayoutTestRoot @@ -89,14 +85,14 @@ namespace Avalonia.Layout.UnitTests root.DoMeasureOverride = MeasureOverride; control1.DoMeasureOverride = MeasureOverride; control2.DoMeasureOverride = MeasureOverride; - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); control2.InvalidateMeasure(); control1.InvalidateMeasure(); root.InvalidateMeasure(); order.Clear(); - target.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.Equal(new ILayoutable[] { root, control1, control2 }, order); } @@ -104,7 +100,6 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Measures_Root_And_Grandparent_In_Correct_Order() { - var target = new LayoutManager(); LayoutTestControl control1; LayoutTestControl control2; var root = new LayoutTestRoot @@ -126,13 +121,13 @@ namespace Avalonia.Layout.UnitTests root.DoMeasureOverride = MeasureOverride; control1.DoMeasureOverride = MeasureOverride; control2.DoMeasureOverride = MeasureOverride; - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); control2.InvalidateMeasure(); root.InvalidateMeasure(); order.Clear(); - target.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.Equal(new ILayoutable[] { root, control2 }, order); } @@ -140,16 +135,15 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Doesnt_Measure_Non_Invalidated_Root() { - var target = new LayoutManager(); var control = new LayoutTestControl(); var root = new LayoutTestRoot { Child = control }; - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); root.Measured = root.Arranged = false; control.Measured = control.Arranged = false; control.InvalidateMeasure(); - target.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.False(root.Measured); Assert.False(root.Arranged); @@ -160,16 +154,15 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Doesnt_Measure_Removed_Control() { - var target = new LayoutManager(); var control = new LayoutTestControl(); var root = new LayoutTestRoot { Child = control }; - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); control.Measured = control.Arranged = false; control.InvalidateMeasure(); root.Child = null; - target.ExecuteLayoutPass(); + root.LayoutManager.ExecuteLayoutPass(); Assert.False(control.Measured); Assert.False(control.Arranged); @@ -178,7 +171,6 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Measures_Root_With_Infinity() { - var target = new LayoutManager(); var root = new LayoutTestRoot(); var availableSize = default(Size); @@ -191,7 +183,7 @@ namespace Avalonia.Layout.UnitTests return new Size(100, 100); }; - target.ExecuteInitialLayoutPass(root); + root.LayoutManager.ExecuteInitialLayoutPass(root); Assert.Equal(Size.Infinity, availableSize); } @@ -199,7 +191,6 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Arranges_Root_With_DesiredSize() { - var target = new LayoutManager(); var root = new LayoutTestRoot { Width = 100, @@ -213,13 +204,13 @@ namespace Avalonia.Layout.UnitTests arrangeSize = s; return s; }; - - target.ExecuteInitialLayoutPass(root); + + root.LayoutManager.ExecuteInitialLayoutPass(root); Assert.Equal(new Size(100, 100), arrangeSize); root.Width = 120; - - target.ExecuteLayoutPass(); + + root.LayoutManager.ExecuteLayoutPass(); Assert.Equal(new Size(120, 100), arrangeSize); } From 5a34acac2d4588d8fce104019a99a76cb2c57cee Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2017 01:58:58 +0200 Subject: [PATCH 08/15] Don't need to do this. We don't need to invalidate newly added controls with the layout manager - this is already handed by the parent control. --- src/Avalonia.Layout/Layoutable.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index bea62efe50..f594450039 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -620,15 +620,6 @@ namespace Avalonia.Layout base.OnVisualParentChanged(oldParent, newParent); } - protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTreeCore(e); - if(!IsMeasureValid) - (VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateMeasure(this); - else if (!IsArrangeValid) - (VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateArrange(this); - } - /// /// Calls on the control on which a property changed. /// From ab30fd343b04382a34bc2e4e70b199f36df2792d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 14 Jun 2017 02:00:12 +0200 Subject: [PATCH 09/15] Handle no previous measure/arrange. This shouldn't happen, but if it does, don't crash. --- src/Avalonia.Layout/LayoutManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index f1f0749d41..73aadcd545 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -165,7 +165,7 @@ namespace Avalonia.Layout { root.Measure(Size.Infinity); } - else + else if (control.PreviousMeasure.HasValue) { control.Measure(control.PreviousMeasure.Value); } @@ -185,7 +185,7 @@ namespace Avalonia.Layout { root.Arrange(new Rect(control.DesiredSize)); } - else + else if (control.PreviousArrange.HasValue) { control.Arrange(control.PreviousArrange.Value); } From 4f26d4fc0865c71a346f526dd13b300df546aa63 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 7 Jun 2018 15:32:57 +0100 Subject: [PATCH 10/15] dont highlight selected menuitems. --- src/Avalonia.Themes.Default/MenuItem.xaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml index efb31175fa..319ff189f0 100644 --- a/src/Avalonia.Themes.Default/MenuItem.xaml +++ b/src/Avalonia.Themes.Default/MenuItem.xaml @@ -122,11 +122,6 @@ - -