From 4e719262c47f9680f599630723814f510d8ddf30 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 19 Mar 2017 20:03:54 +0100 Subject: [PATCH] Make WindowBase.IsVisible return correct value. `WindowBase.IsVisible` now tracks the visibility of the window. Still needs to call `Show` and `Hide` when changed. --- src/Avalonia.Controls/Primitives/PopupRoot.cs | 32 +------ src/Avalonia.Controls/TopLevel.cs | 21 +++-- src/Avalonia.Controls/Window.cs | 25 +++--- src/Avalonia.Controls/WindowBase.cs | 44 +++++++++- .../TopLevelTests.cs | 3 +- .../WindowBaseTests.cs | 64 +++++++++++++- .../WindowTests.cs | 86 +++++++++++++++++++ .../FullLayoutTests.cs | 2 + tests/Avalonia.LeakTests/ControlTests.cs | 14 +++ .../MockWindowingPlatform.cs | 2 +- tests/Avalonia.UnitTests/TestServices.cs | 3 + 11 files changed, 237 insertions(+), 59 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 5e5177b29d..86c1b47521 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -70,32 +70,12 @@ namespace Avalonia.Controls.Primitives this.PlatformImpl.Dispose(); } - /// - /// Hides the popup. - /// - public void Hide() - { - PlatformImpl.Hide(); - IsVisible = false; - } - - /// - /// Shows the popup. - /// - public void Show() - { - EnsureInitialized(); - PlatformImpl.Show(); - LayoutManager.Instance.ExecuteInitialLayoutPass(this); - IsVisible = true; - } - /// protected override void OnTemplateApplied(TemplateAppliedEventArgs e) { base.OnTemplateApplied(e); - if (Parent.TemplatedParent != null) + if (Parent?.TemplatedParent != null) { if (_presenterSubscription != null) { @@ -109,16 +89,6 @@ namespace Avalonia.Controls.Primitives } } - private void EnsureInitialized() - { - if (!this.IsInitialized) - { - var init = (ISupportInitialize)this; - init.BeginInit(); - init.EndInit(); - } - } - private void SetTemplatedParentAndApplyChildTemplates(IControl control) { var templatedParent = Parent.TemplatedParent; diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 4de0256a7d..eaa69b4d4b 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -208,6 +208,16 @@ namespace Avalonia.Controls return PlatformImpl.PointToScreen(p); } + /// + /// Handles a closed notification from . + /// + protected virtual void HandleClosed() + { + Closed?.Invoke(this, EventArgs.Empty); + Renderer?.Dispose(); + Renderer = null; + _applicationLifecycle.OnExit -= OnApplicationExiting; + } /// /// Handles a resize notification from . @@ -267,17 +277,6 @@ namespace Avalonia.Controls return result; } - /// - /// Handles a closed notification from . - /// - private void HandleClosed() - { - Closed?.Invoke(this, EventArgs.Empty); - Renderer?.Dispose(); - Renderer = null; - _applicationLifecycle.OnExit -= OnApplicationExiting; - } - private void OnApplicationExiting(object sender, EventArgs args) { HandleApplicationExiting(); diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index e66b1beb1e..d7a7a9f0eb 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -190,6 +190,7 @@ namespace Avalonia.Controls { s_windows.Remove(this); PlatformImpl.Dispose(); + IsVisible = false; } protected override void HandleApplicationExiting() @@ -216,22 +217,25 @@ namespace Avalonia.Controls /// /// Hides the window but does not close it. /// - public void Hide() + public override void Hide() { using (BeginAutoSizing()) { PlatformImpl.Hide(); } + + IsVisible = false; } /// /// Shows the window. /// - public void Show() + public override void Show() { s_windows.Add(this); EnsureInitialized(); + IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) @@ -265,6 +269,7 @@ namespace Avalonia.Controls s_windows.Add(this); EnsureInitialized(); + IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) @@ -344,6 +349,12 @@ namespace Avalonia.Controls return size; } + protected override void HandleClosed() + { + IsVisible = false; + base.HandleClosed(); + } + /// protected override void HandleResized(Size clientSize) { @@ -354,16 +365,6 @@ namespace Avalonia.Controls base.HandleResized(clientSize); } - - private void EnsureInitialized() - { - if (!this.IsInitialized) - { - var init = (ISupportInitialize)this; - init.BeginInit(); - init.EndInit(); - } - } } } diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index ac6eea8641..4973691716 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -30,6 +30,11 @@ namespace Avalonia.Controls private bool _isActive; + static WindowBase() + { + IsVisibleProperty.OverrideDefaultValue(false); + } + public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current) { } @@ -59,7 +64,6 @@ namespace Avalonia.Controls public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; - /// /// Gets a value that indicates whether the window is active. /// @@ -95,6 +99,25 @@ namespace Avalonia.Controls PlatformImpl.Activate(); } + /// + /// Hides the popup. + /// + public virtual void Hide() + { + PlatformImpl.Hide(); + IsVisible = false; + } + + /// + /// Shows the popup. + /// + public virtual void Show() + { + EnsureInitialized(); + IsVisible = true; + LayoutManager.Instance.ExecuteInitialLayoutPass(this); + PlatformImpl.Show(); + } /// /// Begins an auto-resize operation. @@ -126,6 +149,25 @@ namespace Avalonia.Controls return base.ArrangeOverride(PlatformImpl.ClientSize); } + /// + /// Ensures that the window is initialized. + /// + protected void EnsureInitialized() + { + if (!this.IsInitialized) + { + var init = (ISupportInitialize)this; + init.BeginInit(); + init.EndInit(); + } + } + + protected override void HandleClosed() + { + IsVisible = false; + base.HandleClosed(); + } + /// /// Handles a resize notification from . /// diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index 8ca4ca0fbc..5cd3c57e2e 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -89,6 +89,7 @@ namespace Avalonia.Controls.UnitTests var target = new TestTopLevel(impl.Object) { + IsVisible = true, Template = CreateTemplate(), Content = new TextBlock { @@ -103,8 +104,6 @@ namespace Avalonia.Controls.UnitTests } } - - [Fact] public void Width_And_Height_Should_Not_Be_Set_After_Layout_Pass() { diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs index 43dbcdc610..f565d640b6 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs @@ -34,7 +34,8 @@ namespace Avalonia.Controls.UnitTests { Width = 321, Height = 432, - } + }, + IsVisible = true, }; LayoutManager.Instance.ExecuteInitialLayoutPass(target); @@ -95,6 +96,62 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void IsVisible_Should_Initially_Be_False() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var target = new TestWindowBase(); + + Assert.False(target.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_True_After_Show() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var target = new TestWindowBase(); + + target.Show(); + + Assert.True(target.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_False_Atfer_Hide() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var target = new TestWindowBase(); + + target.Show(); + target.Hide(); + + Assert.False(target.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_False_Atfer_Impl_Signals_Close() + { + var windowImpl = new Mock(); + windowImpl.Setup(x => x.Scaling).Returns(1); + windowImpl.SetupProperty(x => x.Closed); + + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var target = new TestWindowBase(windowImpl.Object); + + target.Show(); + windowImpl.Object.Closed(); + + Assert.False(target.IsVisible); + } + } + private FuncControlTemplate CreateTemplate() { return new FuncControlTemplate(x => @@ -109,6 +166,11 @@ namespace Avalonia.Controls.UnitTests { public bool IsClosed { get; private set; } + public TestWindowBase() + : base(Mock.Of()) + { + } + public TestWindowBase(IWindowBaseImpl impl) : base(impl) { diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index 7b5e3197df..96afecc966 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -28,5 +28,91 @@ namespace Avalonia.Controls.UnitTests windowImpl.Verify(x => x.SetTitle("Hello World")); } } + + [Fact] + public void IsVisible_Should_Initially_Be_False() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var window = new Window(); + + Assert.False(window.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_True_After_Show() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + + window.Show(); + + Assert.True(window.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_True_After_ShowDialog() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + + var task = window.ShowDialog(); + + Assert.True(window.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_False_Atfer_Hide() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + + window.Show(); + window.Hide(); + + Assert.False(window.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_False_Atfer_Close() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + + window.Show(); + window.Close(); + + Assert.False(window.IsVisible); + } + } + + [Fact] + public void IsVisible_Should_Be_False_Atfer_Impl_Signals_Close() + { + var windowImpl = new Mock(); + windowImpl.SetupProperty(x => x.Closed); + windowImpl.Setup(x => x.Scaling).Returns(1); + + var services = TestServices.StyledWindow.With( + windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object)); + + using (UnitTestApplication.Start(services)) + { + var window = new Window(); + + window.Show(); + windowImpl.Object.Closed(); + + Assert.False(window.IsVisible); + } + } } } diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs index 74a1fbc735..d996f881d2 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs @@ -55,6 +55,7 @@ namespace Avalonia.Layout.UnitTests } }; + window.Show(); LayoutManager.Instance.ExecuteInitialLayoutPass(window); Assert.Equal(new Size(400, 400), border.Bounds.Size); @@ -96,6 +97,7 @@ namespace Avalonia.Layout.UnitTests } }; + window.Show(); LayoutManager.Instance.ExecuteInitialLayoutPass(window); Assert.Equal(new Size(800, 600), window.Bounds.Size); diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 23670257ff..1aaba9cf6b 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -42,6 +42,8 @@ namespace Avalonia.LeakTests Content = new Canvas() }; + window.Show(); + // Do a layout and make sure that Canvas gets added to visual tree. LayoutManager.Instance.ExecuteInitialLayoutPass(window); Assert.IsType(window.Presenter.Child); @@ -77,6 +79,8 @@ namespace Avalonia.LeakTests } }; + window.Show(); + // Do a layout and make sure that Canvas gets added to visual tree. LayoutManager.Instance.ExecuteInitialLayoutPass(window); Assert.IsType(window.Find("foo")); @@ -113,6 +117,8 @@ namespace Avalonia.LeakTests } }; + window.Show(); + // Do a layout and make sure that ScrollViewer gets added to visual tree and its // template applied. LayoutManager.Instance.ExecuteInitialLayoutPass(window); @@ -149,6 +155,8 @@ namespace Avalonia.LeakTests Content = new TextBox() }; + window.Show(); + // Do a layout and make sure that TextBox gets added to visual tree and its // template applied. LayoutManager.Instance.ExecuteInitialLayoutPass(window); @@ -192,6 +200,8 @@ namespace Avalonia.LeakTests var textBox = (TextBox)window.Content; textBox.Bind(TextBox.TextProperty, binding); + window.Show(); + // Do a layout and make sure that TextBox gets added to visual tree and its // Text property set. LayoutManager.Instance.ExecuteInitialLayoutPass(window); @@ -229,6 +239,8 @@ namespace Avalonia.LeakTests Content = textBox = new TextBox() }; + window.Show(); + // Do a layout and make sure that TextBox gets added to visual tree and its // template applied. LayoutManager.Instance.ExecuteInitialLayoutPass(window); @@ -282,6 +294,8 @@ namespace Avalonia.LeakTests } }; + window.Show(); + // Do a layout and make sure that TreeViewItems get realized. LayoutManager.Instance.ExecuteInitialLayoutPass(window); Assert.Equal(1, target.ItemContainerGenerator.Containers.Count()); diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 8f75ec8efc..36297bf58b 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -25,6 +25,6 @@ namespace Avalonia.UnitTests throw new NotImplementedException(); } - public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of(); + public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of(x => x.Scaling == 1); } } \ No newline at end of file diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs index 7b3be3421d..3efbd6c9be 100644 --- a/tests/Avalonia.UnitTests/TestServices.cs +++ b/tests/Avalonia.UnitTests/TestServices.cs @@ -43,6 +43,9 @@ namespace Avalonia.UnitTests public static readonly TestServices MockThreadingInterface = new TestServices( threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)); + public static readonly TestServices MockWindowingPlatform = new TestServices( + windowingPlatform: new MockWindowingPlatform()); + public static readonly TestServices RealFocus = new TestServices( focusManager: new FocusManager(), keyboardDevice: () => new KeyboardDevice(),