Browse Source

Merge pull request #935 from AvaloniaUI/window-isvisible

Make setting WindowBase.IsVisible track window visibility.
pull/940/head
Steven Kirk 9 years ago
committed by GitHub
parent
commit
ea8ac9c483
  1. 32
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  2. 21
      src/Avalonia.Controls/TopLevel.cs
  3. 25
      src/Avalonia.Controls/Window.cs
  4. 88
      src/Avalonia.Controls/WindowBase.cs
  5. 3
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  6. 95
      tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
  7. 86
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  8. 2
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  9. 14
      tests/Avalonia.LeakTests/ControlTests.cs
  10. 2
      tests/Avalonia.UnitTests/MockWindowingPlatform.cs
  11. 3
      tests/Avalonia.UnitTests/TestServices.cs

32
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -70,32 +70,12 @@ namespace Avalonia.Controls.Primitives
this.PlatformImpl.Dispose(); this.PlatformImpl.Dispose();
} }
/// <summary>
/// Hides the popup.
/// </summary>
public void Hide()
{
PlatformImpl.Hide();
IsVisible = false;
}
/// <summary>
/// Shows the popup.
/// </summary>
public void Show()
{
EnsureInitialized();
PlatformImpl.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
IsVisible = true;
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{ {
base.OnTemplateApplied(e); base.OnTemplateApplied(e);
if (Parent.TemplatedParent != null) if (Parent?.TemplatedParent != null)
{ {
if (_presenterSubscription != 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) private void SetTemplatedParentAndApplyChildTemplates(IControl control)
{ {
var templatedParent = Parent.TemplatedParent; var templatedParent = Parent.TemplatedParent;

21
src/Avalonia.Controls/TopLevel.cs

@ -208,6 +208,16 @@ namespace Avalonia.Controls
return PlatformImpl.PointToScreen(p); return PlatformImpl.PointToScreen(p);
} }
/// <summary>
/// Handles a closed notification from <see cref="ITopLevelImpl.Closed"/>.
/// </summary>
protected virtual void HandleClosed()
{
Closed?.Invoke(this, EventArgs.Empty);
Renderer?.Dispose();
Renderer = null;
_applicationLifecycle.OnExit -= OnApplicationExiting;
}
/// <summary> /// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>. /// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
@ -267,17 +277,6 @@ namespace Avalonia.Controls
return result; return result;
} }
/// <summary>
/// Handles a closed notification from <see cref="ITopLevelImpl.Closed"/>.
/// </summary>
private void HandleClosed()
{
Closed?.Invoke(this, EventArgs.Empty);
Renderer?.Dispose();
Renderer = null;
_applicationLifecycle.OnExit -= OnApplicationExiting;
}
private void OnApplicationExiting(object sender, EventArgs args) private void OnApplicationExiting(object sender, EventArgs args)
{ {
HandleApplicationExiting(); HandleApplicationExiting();

25
src/Avalonia.Controls/Window.cs

@ -190,6 +190,7 @@ namespace Avalonia.Controls
{ {
s_windows.Remove(this); s_windows.Remove(this);
PlatformImpl.Dispose(); PlatformImpl.Dispose();
IsVisible = false;
} }
protected override void HandleApplicationExiting() protected override void HandleApplicationExiting()
@ -216,22 +217,25 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Hides the window but does not close it. /// Hides the window but does not close it.
/// </summary> /// </summary>
public void Hide() public override void Hide()
{ {
using (BeginAutoSizing()) using (BeginAutoSizing())
{ {
PlatformImpl.Hide(); PlatformImpl.Hide();
} }
IsVisible = false;
} }
/// <summary> /// <summary>
/// Shows the window. /// Shows the window.
/// </summary> /// </summary>
public void Show() public override void Show()
{ {
s_windows.Add(this); s_windows.Add(this);
EnsureInitialized(); EnsureInitialized();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this); LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing()) using (BeginAutoSizing())
@ -265,6 +269,7 @@ namespace Avalonia.Controls
s_windows.Add(this); s_windows.Add(this);
EnsureInitialized(); EnsureInitialized();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this); LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing()) using (BeginAutoSizing())
@ -344,6 +349,12 @@ namespace Avalonia.Controls
return size; return size;
} }
protected override void HandleClosed()
{
IsVisible = false;
base.HandleClosed();
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void HandleResized(Size clientSize) protected override void HandleResized(Size clientSize)
{ {
@ -354,16 +365,6 @@ namespace Avalonia.Controls
base.HandleResized(clientSize); base.HandleResized(clientSize);
} }
private void EnsureInitialized()
{
if (!this.IsInitialized)
{
var init = (ISupportInitialize)this;
init.BeginInit();
init.EndInit();
}
}
} }
} }

88
src/Avalonia.Controls/WindowBase.cs

@ -29,6 +29,13 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<WindowBase, bool>(nameof(IsActive), o => o.IsActive); AvaloniaProperty.RegisterDirect<WindowBase, bool>(nameof(IsActive), o => o.IsActive);
private bool _isActive; private bool _isActive;
private bool _ignoreVisibilityChange;
static WindowBase()
{
IsVisibleProperty.OverrideDefaultValue<WindowBase>(false);
IsVisibleProperty.Changed.AddClassHandler<WindowBase>(x => x.IsVisibleChanged);
}
public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current) public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
{ {
@ -59,7 +66,6 @@ namespace Avalonia.Controls
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
/// <summary> /// <summary>
/// Gets a value that indicates whether the window is active. /// Gets a value that indicates whether the window is active.
/// </summary> /// </summary>
@ -95,6 +101,43 @@ namespace Avalonia.Controls
PlatformImpl.Activate(); PlatformImpl.Activate();
} }
/// <summary>
/// Hides the popup.
/// </summary>
public virtual void Hide()
{
_ignoreVisibilityChange = true;
try
{
PlatformImpl.Hide();
IsVisible = false;
}
finally
{
_ignoreVisibilityChange = false;
}
}
/// <summary>
/// Shows the popup.
/// </summary>
public virtual void Show()
{
_ignoreVisibilityChange = true;
try
{
EnsureInitialized();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
PlatformImpl.Show();
}
finally
{
_ignoreVisibilityChange = false;
}
}
/// <summary> /// <summary>
/// Begins an auto-resize operation. /// Begins an auto-resize operation.
@ -126,6 +169,34 @@ namespace Avalonia.Controls
return base.ArrangeOverride(PlatformImpl.ClientSize); return base.ArrangeOverride(PlatformImpl.ClientSize);
} }
/// <summary>
/// Ensures that the window is initialized.
/// </summary>
protected void EnsureInitialized()
{
if (!this.IsInitialized)
{
var init = (ISupportInitialize)this;
init.BeginInit();
init.EndInit();
}
}
protected override void HandleClosed()
{
_ignoreVisibilityChange = true;
try
{
IsVisible = false;
base.HandleClosed();
}
finally
{
_ignoreVisibilityChange = false;
}
}
/// <summary> /// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>. /// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary> /// </summary>
@ -180,6 +251,21 @@ namespace Avalonia.Controls
Deactivated?.Invoke(this, EventArgs.Empty); Deactivated?.Invoke(this, EventArgs.Empty);
} }
private void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
{
if (!_ignoreVisibilityChange)
{
if ((bool)e.NewValue)
{
Show();
}
else
{
Hide();
}
}
}
/// <summary> /// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// Starts moving a window with left button being held. Should be called from left mouse button press event handler
/// </summary> /// </summary>

3
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -89,6 +89,7 @@ namespace Avalonia.Controls.UnitTests
var target = new TestTopLevel(impl.Object) var target = new TestTopLevel(impl.Object)
{ {
IsVisible = true,
Template = CreateTemplate(), Template = CreateTemplate(),
Content = new TextBlock Content = new TextBlock
{ {
@ -103,8 +104,6 @@ namespace Avalonia.Controls.UnitTests
} }
} }
[Fact] [Fact]
public void Width_And_Height_Should_Not_Be_Set_After_Layout_Pass() public void Width_And_Height_Should_Not_Be_Set_After_Layout_Pass()
{ {

95
tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs

@ -34,7 +34,8 @@ namespace Avalonia.Controls.UnitTests
{ {
Width = 321, Width = 321,
Height = 432, Height = 432,
} },
IsVisible = true,
}; };
LayoutManager.Instance.ExecuteInitialLayoutPass(target); LayoutManager.Instance.ExecuteInitialLayoutPass(target);
@ -95,6 +96,93 @@ 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<IPopupImpl>();
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);
}
}
[Fact]
public void Setting_IsVisible_True_Shows_Window()
{
var windowImpl = new Mock<IPopupImpl>();
windowImpl.Setup(x => x.Scaling).Returns(1);
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new TestWindowBase(windowImpl.Object);
target.IsVisible = true;
windowImpl.Verify(x => x.Show());
}
}
[Fact]
public void Setting_IsVisible_False_Hides_Window()
{
var windowImpl = new Mock<IPopupImpl>();
windowImpl.Setup(x => x.Scaling).Returns(1);
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new TestWindowBase(windowImpl.Object);
target.Show();
target.IsVisible = false;
windowImpl.Verify(x => x.Hide());
}
}
private FuncControlTemplate<TestWindowBase> CreateTemplate() private FuncControlTemplate<TestWindowBase> CreateTemplate()
{ {
return new FuncControlTemplate<TestWindowBase>(x => return new FuncControlTemplate<TestWindowBase>(x =>
@ -109,6 +197,11 @@ namespace Avalonia.Controls.UnitTests
{ {
public bool IsClosed { get; private set; } public bool IsClosed { get; private set; }
public TestWindowBase()
: base(Mock.Of<IWindowBaseImpl>())
{
}
public TestWindowBase(IWindowBaseImpl impl) public TestWindowBase(IWindowBaseImpl impl)
: base(impl) : base(impl)
{ {

86
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -28,5 +28,91 @@ namespace Avalonia.Controls.UnitTests
windowImpl.Verify(x => x.SetTitle("Hello World")); 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<IWindowImpl>();
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);
}
}
} }
} }

2
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@ -55,6 +55,7 @@ namespace Avalonia.Layout.UnitTests
} }
}; };
window.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.Equal(new Size(400, 400), border.Bounds.Size); Assert.Equal(new Size(400, 400), border.Bounds.Size);
@ -96,6 +97,7 @@ namespace Avalonia.Layout.UnitTests
} }
}; };
window.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.Equal(new Size(800, 600), window.Bounds.Size); Assert.Equal(new Size(800, 600), window.Bounds.Size);

14
tests/Avalonia.LeakTests/ControlTests.cs

@ -42,6 +42,8 @@ namespace Avalonia.LeakTests
Content = new Canvas() Content = new Canvas()
}; };
window.Show();
// Do a layout and make sure that Canvas gets added to visual tree. // Do a layout and make sure that Canvas gets added to visual tree.
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.IsType<Canvas>(window.Presenter.Child); Assert.IsType<Canvas>(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. // Do a layout and make sure that Canvas gets added to visual tree.
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.IsType<Canvas>(window.Find<Canvas>("foo")); Assert.IsType<Canvas>(window.Find<Canvas>("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 // Do a layout and make sure that ScrollViewer gets added to visual tree and its
// template applied. // template applied.
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
@ -149,6 +155,8 @@ namespace Avalonia.LeakTests
Content = new TextBox() Content = new TextBox()
}; };
window.Show();
// Do a layout and make sure that TextBox gets added to visual tree and its // Do a layout and make sure that TextBox gets added to visual tree and its
// template applied. // template applied.
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
@ -192,6 +200,8 @@ namespace Avalonia.LeakTests
var textBox = (TextBox)window.Content; var textBox = (TextBox)window.Content;
textBox.Bind(TextBox.TextProperty, binding); textBox.Bind(TextBox.TextProperty, binding);
window.Show();
// Do a layout and make sure that TextBox gets added to visual tree and its // Do a layout and make sure that TextBox gets added to visual tree and its
// Text property set. // Text property set.
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
@ -229,6 +239,8 @@ namespace Avalonia.LeakTests
Content = textBox = new TextBox() Content = textBox = new TextBox()
}; };
window.Show();
// Do a layout and make sure that TextBox gets added to visual tree and its // Do a layout and make sure that TextBox gets added to visual tree and its
// template applied. // template applied.
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
@ -282,6 +294,8 @@ namespace Avalonia.LeakTests
} }
}; };
window.Show();
// Do a layout and make sure that TreeViewItems get realized. // Do a layout and make sure that TreeViewItems get realized.
LayoutManager.Instance.ExecuteInitialLayoutPass(window); LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count()); Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());

2
tests/Avalonia.UnitTests/MockWindowingPlatform.cs

@ -25,6 +25,6 @@ namespace Avalonia.UnitTests
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of<IPopupImpl>(); public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of<IPopupImpl>(x => x.Scaling == 1);
} }
} }

3
tests/Avalonia.UnitTests/TestServices.cs

@ -43,6 +43,9 @@ namespace Avalonia.UnitTests
public static readonly TestServices MockThreadingInterface = new TestServices( public static readonly TestServices MockThreadingInterface = new TestServices(
threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true)); threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true));
public static readonly TestServices MockWindowingPlatform = new TestServices(
windowingPlatform: new MockWindowingPlatform());
public static readonly TestServices RealFocus = new TestServices( public static readonly TestServices RealFocus = new TestServices(
focusManager: new FocusManager(), focusManager: new FocusManager(),
keyboardDevice: () => new KeyboardDevice(), keyboardDevice: () => new KeyboardDevice(),

Loading…
Cancel
Save