Browse Source
Conflicts: src/Gtk/Avalonia.Gtk/TopLevelImpl.cs tests/Avalonia.Controls.UnitTests/TopLevelTests.csscenegraph-after-breakage
41 changed files with 595 additions and 583 deletions
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Platform |
|||
{ |
|||
public interface IWindowBaseImpl : ITopLevelImpl |
|||
{ |
|||
/// <summary>
|
|||
/// Shows the toplevel.
|
|||
/// </summary>
|
|||
void Show(); |
|||
|
|||
/// <summary>
|
|||
/// Hides the window.
|
|||
/// </summary>
|
|||
void Hide(); |
|||
|
|||
/// <summary>
|
|||
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
|
|||
/// </summary>
|
|||
void BeginMoveDrag(); |
|||
|
|||
/// <summary>
|
|||
/// Starts resizing a window. This function is used if an application has window resizing controls.
|
|||
/// Should be called from left mouse button press event handler
|
|||
/// </summary>
|
|||
void BeginResizeDrag(WindowEdge edge); |
|||
|
|||
/// <summary>
|
|||
/// Gets position of the window relatively to the screen
|
|||
/// </summary>
|
|||
Point Position { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a method called when the window's position changes.
|
|||
/// </summary>
|
|||
Action<Point> PositionChanged { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Activates the window.
|
|||
/// </summary>
|
|||
void Activate(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a method called when the window is deactivated (loses focus).
|
|||
/// </summary>
|
|||
Action Deactivated { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a method called when the window is activated (receives focus).
|
|||
/// </summary>
|
|||
Action Activated { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the platform window handle.
|
|||
/// </summary>
|
|||
IPlatformHandle Handle { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the maximum size of a window on the system.
|
|||
/// </summary>
|
|||
Size MaxClientSize { get; } |
|||
|
|||
/// <summary>
|
|||
/// Sets the client size of the toplevel.
|
|||
/// </summary>
|
|||
void Resize(Size clientSize); |
|||
} |
|||
} |
|||
@ -0,0 +1,194 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reactive.Disposables; |
|||
using System.Reactive.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Input; |
|||
using Avalonia.Layout; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Base class for top-level windows.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This class acts as a base for top level windows such as <see cref="Window"/> and
|
|||
/// <see cref="PopupRoot"/>. It handles scheduling layout, styling and rendering as well as
|
|||
/// tracking the window <see cref="TopLevel.ClientSize"/> and <see cref="IsActive"/> state.
|
|||
/// </remarks>
|
|||
public class WindowBase : TopLevel |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the <see cref="IsActive"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<WindowBase, bool> IsActiveProperty = |
|||
AvaloniaProperty.RegisterDirect<WindowBase, bool>(nameof(IsActive), o => o.IsActive); |
|||
|
|||
private bool _isActive; |
|||
|
|||
public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current) |
|||
{ |
|||
} |
|||
|
|||
public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) |
|||
{ |
|||
PlatformImpl.Activated = HandleActivated; |
|||
PlatformImpl.Deactivated = HandleDeactivated; |
|||
PlatformImpl.PositionChanged = HandlePositionChanged; |
|||
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fired when the window is activated.
|
|||
/// </summary>
|
|||
public event EventHandler Activated; |
|||
|
|||
/// <summary>
|
|||
/// Fired when the window is deactivated.
|
|||
/// </summary>
|
|||
public event EventHandler Deactivated; |
|||
|
|||
/// <summary>
|
|||
/// Fired when the window position is changed.
|
|||
/// </summary>
|
|||
public event EventHandler<PointEventArgs> PositionChanged; |
|||
|
|||
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the window is active.
|
|||
/// </summary>
|
|||
public bool IsActive |
|||
{ |
|||
get { return _isActive; } |
|||
private set { SetAndRaise(IsActiveProperty, ref _isActive, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the window position in screen coordinates.
|
|||
/// </summary>
|
|||
public Point Position |
|||
{ |
|||
get { return PlatformImpl.Position; } |
|||
set { PlatformImpl.Position = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Whether an auto-size operation is in progress.
|
|||
/// </summary>
|
|||
protected bool AutoSizing |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Activates the window.
|
|||
/// </summary>
|
|||
public void Activate() |
|||
{ |
|||
PlatformImpl.Activate(); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Begins an auto-resize operation.
|
|||
/// </summary>
|
|||
/// <returns>A disposable used to finish the operation.</returns>
|
|||
/// <remarks>
|
|||
/// When an auto-resize operation is in progress any resize events received will not be
|
|||
/// cause the new size to be written to the <see cref="Layoutable.Width"/> and
|
|||
/// <see cref="Layoutable.Height"/> properties.
|
|||
/// </remarks>
|
|||
protected IDisposable BeginAutoSizing() |
|||
{ |
|||
AutoSizing = true; |
|||
return Disposable.Create(() => AutoSizing = false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Carries out the arrange pass of the window.
|
|||
/// </summary>
|
|||
/// <param name="finalSize">The final window size.</param>
|
|||
/// <returns>The <paramref name="finalSize"/> parameter unchanged.</returns>
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
using (BeginAutoSizing()) |
|||
{ |
|||
PlatformImpl.Resize(finalSize); |
|||
} |
|||
|
|||
return base.ArrangeOverride(PlatformImpl.ClientSize); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
|
|||
/// </summary>
|
|||
/// <param name="clientSize">The new client size.</param>
|
|||
protected override void HandleResized(Size clientSize) |
|||
{ |
|||
if (!AutoSizing) |
|||
{ |
|||
Width = clientSize.Width; |
|||
Height = clientSize.Height; |
|||
} |
|||
ClientSize = clientSize; |
|||
LayoutManager.Instance.ExecuteLayoutPass(); |
|||
PlatformImpl.Invalidate(new Rect(clientSize)); |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handles a window position change notification from
|
|||
/// <see cref="IWindowBaseImpl.PositionChanged"/>.
|
|||
/// </summary>
|
|||
/// <param name="pos">The window position.</param>
|
|||
private void HandlePositionChanged(Point pos) |
|||
{ |
|||
PositionChanged?.Invoke(this, new PointEventArgs(pos)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handles an activated notification from <see cref="IWindowBaseImpl.Activated"/>.
|
|||
/// </summary>
|
|||
private void HandleActivated() |
|||
{ |
|||
Activated?.Invoke(this, EventArgs.Empty); |
|||
|
|||
var scope = this as IFocusScope; |
|||
|
|||
if (scope != null) |
|||
{ |
|||
FocusManager.Instance.SetFocusScope(scope); |
|||
} |
|||
|
|||
IsActive = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handles a deactivated notification from <see cref="IWindowBaseImpl.Deactivated"/>.
|
|||
/// </summary>
|
|||
private void HandleDeactivated() |
|||
{ |
|||
IsActive = false; |
|||
|
|||
Deactivated?.Invoke(this, EventArgs.Empty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler
|
|||
/// </summary>
|
|||
public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag(); |
|||
|
|||
/// <summary>
|
|||
/// Starts resizing a window. This function is used if an application has window resizing controls.
|
|||
/// Should be called from left mouse button press event handler
|
|||
/// </summary>
|
|||
public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge); |
|||
} |
|||
} |
|||
@ -0,0 +1,126 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Reactive; |
|||
using System.Reactive.Subjects; |
|||
using Moq; |
|||
using Avalonia.Controls.Presenters; |
|||
using Avalonia.Controls.Templates; |
|||
using Avalonia.Input; |
|||
using Avalonia.Input.Raw; |
|||
using Avalonia.Layout; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering; |
|||
using Avalonia.Styling; |
|||
using Avalonia.UnitTests; |
|||
using Ploeh.AutoFixture; |
|||
using Ploeh.AutoFixture.AutoMoq; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Controls.UnitTests |
|||
{ |
|||
public class WindowBaseTests |
|||
{ |
|||
[Fact] |
|||
public void Impl_ClientSize_Should_Be_Set_After_Layout_Pass() |
|||
{ |
|||
using (UnitTestApplication.Start(TestServices.StyledWindow)) |
|||
{ |
|||
var impl = Mock.Of<IWindowBaseImpl>(x => x.Scaling == 1); |
|||
|
|||
var target = new TestWindowBase(impl) |
|||
{ |
|||
Template = CreateTemplate(), |
|||
Content = new TextBlock |
|||
{ |
|||
Width = 321, |
|||
Height = 432, |
|||
} |
|||
}; |
|||
|
|||
LayoutManager.Instance.ExecuteInitialLayoutPass(target); |
|||
|
|||
Mock.Get(impl).Verify(x => x.Resize(new Size(321, 432))); |
|||
} |
|||
} |
|||
|
|||
|
|||
[Fact] |
|||
public void Activate_Should_Call_Impl_Activate() |
|||
{ |
|||
using (UnitTestApplication.Start(TestServices.StyledWindow)) |
|||
{ |
|||
var impl = new Mock<IWindowBaseImpl>(); |
|||
var target = new TestWindowBase(impl.Object); |
|||
|
|||
target.Activate(); |
|||
|
|||
impl.Verify(x => x.Activate()); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Impl_Activate_Should_Call_Raise_Activated_Event() |
|||
{ |
|||
using (UnitTestApplication.Start(TestServices.StyledWindow)) |
|||
{ |
|||
var impl = new Mock<IWindowBaseImpl>(); |
|||
impl.SetupAllProperties(); |
|||
|
|||
bool raised = false; |
|||
var target = new TestWindowBase(impl.Object); |
|||
target.Activated += (s, e) => raised = true; |
|||
|
|||
impl.Object.Activated(); |
|||
|
|||
Assert.True(raised); |
|||
} |
|||
} |
|||
|
|||
|
|||
[Fact] |
|||
public void Impl_Deactivate_Should_Call_Raise_Deativated_Event() |
|||
{ |
|||
using (UnitTestApplication.Start(TestServices.StyledWindow)) |
|||
{ |
|||
var impl = new Mock<IWindowBaseImpl>(); |
|||
impl.SetupAllProperties(); |
|||
|
|||
bool raised = false; |
|||
var target = new TestWindowBase(impl.Object); |
|||
target.Deactivated += (s, e) => raised = true; |
|||
|
|||
impl.Object.Deactivated(); |
|||
|
|||
Assert.True(raised); |
|||
} |
|||
} |
|||
|
|||
private FuncControlTemplate<TestWindowBase> CreateTemplate() |
|||
{ |
|||
return new FuncControlTemplate<TestWindowBase>(x => |
|||
new ContentPresenter |
|||
{ |
|||
Name = "PART_ContentPresenter", |
|||
[!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty], |
|||
}); |
|||
} |
|||
|
|||
private class TestWindowBase : WindowBase |
|||
{ |
|||
public bool IsClosed { get; private set; } |
|||
|
|||
public TestWindowBase(IWindowBaseImpl impl) |
|||
: base(impl) |
|||
{ |
|||
} |
|||
|
|||
protected override void HandleApplicationExiting() |
|||
{ |
|||
base.HandleApplicationExiting(); |
|||
IsClosed = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue