Browse Source

Refactored out WindowBase from TopLevel

pull/899/head
Nikita Tsukanov 9 years ago
parent
commit
f21212e76a
  1. 6
      Avalonia.sln
  2. 16
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  3. 4
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  4. 2
      src/Avalonia.Controls/Avalonia.Controls.csproj
  5. 4
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  6. 12
      src/Avalonia.Controls/Menu.cs
  7. 2
      src/Avalonia.Controls/Platform/IEmbeddableWindowImpl.cs
  8. 2
      src/Avalonia.Controls/Platform/IPopupImpl.cs
  9. 78
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  10. 69
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  11. 2
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  12. 2
      src/Avalonia.Controls/Platform/PlatformManager.cs
  13. 14
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  14. 10
      src/Avalonia.Controls/Primitives/Popup.cs
  15. 2
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  16. 155
      src/Avalonia.Controls/TopLevel.cs
  17. 2
      src/Avalonia.Controls/Window.cs
  18. 191
      src/Avalonia.Controls/WindowBase.cs
  19. 3
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  20. 7
      src/Gtk/Avalonia.Gtk/EmbeddableImpl.cs
  21. 8
      src/Gtk/Avalonia.Gtk/WindowImpl.cs
  22. 6
      src/Gtk/Avalonia.Gtk/WindowImplBase.cs
  23. 2
      src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
  24. 4
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  25. 2
      src/Gtk/Avalonia.Gtk3/PopupImpl.cs
  26. 4
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  27. 17
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  28. 2
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  29. 10
      src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
  30. 4
      src/Windows/Avalonia.Win32/Win32Platform.cs
  31. 30
      src/Windows/Avalonia.Win32/WindowImpl.cs

6
Avalonia.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.26127.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject
@ -169,13 +169,13 @@ Global
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{40759a76-d0f2-464e-8000-6ff0f5c4bd7c}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{48840edd-24bf-495d-911e-2eb12ae75d3b}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7863ea94-f0fb-4386-bf8c-e5bfa761560a}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 4
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 4
src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4

16
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -18,21 +18,19 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
private Point _position;
private bool _isAdded;
Action IWindowBaseImpl.Activated { get; set; }
public PopupImpl() : base(ActivityTracker.Current, true)
{
}
private Size _clientSize = new Size(1, 1);
public override Size ClientSize
public void Resize(Size value)
{
get { return base.ClientSize; }
set
{
if(View == null)
return;
_clientSize = value;
UpdateParams();
}
if (View == null)
return;
_clientSize = value;
UpdateParams();
}
public override Point Position

4
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -83,9 +83,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public View View => _view;
Action ITopLevelImpl.Activated { get; set; }
IPlatformHandle ITopLevelImpl.Handle => _view;
public IPlatformHandle Handle => _view;
public IEnumerable<object> Surfaces => new object[] {this};

2
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -57,6 +57,7 @@
<Compile Include="HotkeyManager.cs" />
<Compile Include="IApplicationLifecycle.cs" />
<Compile Include="IScrollable.cs" />
<Compile Include="Platform\IWindowBaseImpl.cs" />
<Compile Include="Platform\Surfaces\IFramebufferPlatformSurface.cs" />
<Compile Include="Platform\Surfaces\ILockedFramebuffer.cs" />
<Compile Include="Platform\Surfaces\PixelFormat.cs" />
@ -65,6 +66,7 @@
<Compile Include="Platform\IEmbeddableWindowImpl.cs" />
<Compile Include="Platform\ExportAvaloniaModuleAttribute.cs" />
<Compile Include="Platform\ExportWindowingSubsystemAttribute.cs" />
<Compile Include="WindowBase.cs" />
<Compile Include="WindowIcon.cs" />
<Compile Include="IPseudoClasses.cs" />
<Compile Include="DropDownItem.cs" />

4
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@ -11,12 +11,11 @@ namespace Avalonia.Controls.Embedding
{
public EmbeddableControlRoot(IEmbeddableWindowImpl impl) : base(impl)
{
PlatformImpl.Show();
}
public EmbeddableControlRoot() : base(PlatformManager.CreateEmbeddableWindow())
{
PlatformImpl.Show();
}
public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl;
@ -25,7 +24,6 @@ namespace Avalonia.Controls.Embedding
{
EnsureInitialized();
ApplyTemplate();
PlatformImpl.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
}

12
src/Avalonia.Controls/Menu.cs

@ -103,9 +103,11 @@ namespace Avalonia.Controls
{
base.OnAttachedToVisualTree(e);
var topLevel = e.Root as TopLevel;
var topLevel = (TopLevel)e.Root;
var window = e.Root as Window;
topLevel.Deactivated += Deactivated;
if (window != null)
window.Deactivated += Deactivated;
var pointerPress = topLevel.AddHandler(
PointerPressedEvent,
@ -114,7 +116,11 @@ namespace Avalonia.Controls
_subscription = new CompositeDisposable(
pointerPress,
Disposable.Create(() => topLevel.Deactivated -= Deactivated),
Disposable.Create(() =>
{
if (window != null)
window.Deactivated -= Deactivated;
}),
InputManager.Instance.Process.Subscribe(ListenForNonClientClick));
var inputRoot = e.Root as IInputRoot;

2
src/Avalonia.Controls/Platform/IEmbeddableWindowImpl.cs

@ -8,7 +8,7 @@ namespace Avalonia.Platform
/// <summary>
/// Defines a platform-specific embeddable window implementation.
/// </summary>
public interface IEmbeddableWindowImpl : IWindowImpl
public interface IEmbeddableWindowImpl : ITopLevelImpl
{
event Action LostFocus;
}

2
src/Avalonia.Controls/Platform/IPopupImpl.cs

@ -6,7 +6,7 @@ namespace Avalonia.Platform
/// <summary>
/// Defines a platform-specific popup window implementation.
/// </summary>
public interface IPopupImpl : ITopLevelImpl
public interface IPopupImpl : IWindowBaseImpl
{
}

78
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -19,25 +18,15 @@ namespace Avalonia.Platform
public interface ITopLevelImpl : IDisposable
{
/// <summary>
/// Gets or sets the client size of the window.
/// Gets the client size of the toplevel.
/// </summary>
Size ClientSize { get; set; }
Size ClientSize { get; }
/// <summary>
/// Gets the maximum size of a window on the system.
/// </summary>
Size MaxClientSize { get; }
/// <summary>
/// Gets the scaling factor for the window.
/// Gets the scaling factor for the toplevel.
/// </summary>
double Scaling { get; }
/// <summary>
/// Gets the platform window handle.
/// </summary>
IPlatformHandle Handle { get; }
/// <summary>
/// The list of native platform's surfaces that can be consumed by rendering subsystems.
/// </summary>
@ -51,57 +40,32 @@ namespace Avalonia.Platform
IEnumerable<object> Surfaces { get; }
/// <summary>
/// Gets or sets a method called when the window is activated (receives focus).
/// </summary>
Action Activated { get; set; }
/// <summary>
/// Gets or sets a method called when the window is closed.
/// </summary>
Action Closed { get; set; }
/// <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 receives input.
/// Gets or sets a method called when the toplevel receives input.
/// </summary>
Action<RawInputEventArgs> Input { get; set; }
/// <summary>
/// Gets or sets a method called when the window requires painting.
/// Gets or sets a method called when the toplevel requires painting.
/// </summary>
Action<Rect> Paint { get; set; }
/// <summary>
/// Gets or sets a method called when the window is resized.
/// Gets or sets a method called when the toplevel is resized.
/// </summary>
Action<Size> Resized { get; set; }
/// <summary>
/// Gets or sets a method called when the window's scaling changes.
/// Gets or sets a method called when the toplevel's scaling changes.
/// </summary>
Action<double> ScalingChanged { 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>
/// Invalidates a rect on the window.
/// Invalidates a rect on the toplevel.
/// </summary>
void Invalidate(Rect rect);
/// <summary>
/// Sets the <see cref="IInputRoot"/> for the window.
/// Sets the <see cref="IInputRoot"/> for the toplevel.
/// </summary>
void SetInputRoot(IInputRoot inputRoot);
@ -120,32 +84,14 @@ namespace Avalonia.Platform
Point PointToScreen(Point point);
/// <summary>
/// Sets the cursor associated with the window.
/// Sets the cursor associated with the toplevel.
/// </summary>
/// <param name="cursor">The cursor. Use null for default cursor</param>
void SetCursor(IPlatformHandle cursor);
/// <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
/// Gets or sets a method called when the underlying implementation is destroyed.
/// </summary>
void BeginResizeDrag(WindowEdge edge);
Point Position { get; set; }
Action Closed { get; set; }
}
}

69
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -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>
/// Gets the client size of the toplevel.
/// </summary>
void Resize(Size size);
}
}

2
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -9,7 +9,7 @@ namespace Avalonia.Platform
/// <summary>
/// Defines a platform-specific window implementation.
/// </summary>
public interface IWindowImpl : ITopLevelImpl
public interface IWindowImpl : IWindowBaseImpl
{
/// <summary>
/// Gets or sets the minimized/maximized state of the window.

2
src/Avalonia.Controls/Platform/PlatformManager.cs

@ -31,7 +31,7 @@ namespace Avalonia.Controls.Platform
throw new Exception("Could not CreateWindow(): IWindowingPlatform is not registered.");
}
return s_designerMode ? platform.CreateEmbeddableWindow() : platform.CreateWindow();
return s_designerMode ? (IWindowImpl)platform.CreateEmbeddableWindow() : platform.CreateWindow();
}
public static IEmbeddableWindowImpl CreateEmbeddableWindow()

14
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -97,7 +97,11 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/>
public override Size MeasureOverride(Size availableSize)
{
var window = Owner.GetVisualRoot() as TopLevel;
var maxAvailableSize = (Owner.GetVisualRoot() as WindowBase)?.PlatformImpl?.MaxClientSize;
if (!maxAvailableSize.HasValue)
{
maxAvailableSize = (Owner.GetVisualRoot() as TopLevel)?.ClientSize;
}
// If infinity is passed as the available size and we're virtualized then we need to
// fill the available space, but to do that we *don't* want to materialize all our
@ -107,9 +111,9 @@ namespace Avalonia.Controls.Presenters
{
if (availableSize.Height == double.PositiveInfinity)
{
if (window != null)
if (maxAvailableSize.HasValue)
{
availableSize = availableSize.WithHeight(window.PlatformImpl.MaxClientSize.Height);
availableSize = availableSize.WithHeight(maxAvailableSize.Value.Height);
}
}
@ -119,9 +123,9 @@ namespace Avalonia.Controls.Presenters
{
if (availableSize.Width == double.PositiveInfinity)
{
if (window != null)
if (maxAvailableSize.HasValue)
{
availableSize = availableSize.WithWidth(window.PlatformImpl.MaxClientSize.Width);
availableSize = availableSize.WithWidth(maxAvailableSize.Value.Width);
}
}

10
src/Avalonia.Controls/Primitives/Popup.cs

@ -213,7 +213,9 @@ namespace Avalonia.Controls.Primitives
if (_topLevel != null)
{
_topLevel.Deactivated += TopLevelDeactivated;
var window = _topLevel as Window;
if (window != null)
window.Deactivated += WindowDeactivated;
_topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
_nonClientListener = InputManager.Instance.Process.Subscribe(ListenForNonClientClick);
}
@ -239,7 +241,9 @@ namespace Avalonia.Controls.Primitives
if (_topLevel != null)
{
_topLevel.RemoveHandler(PointerPressedEvent, PointerPressedOutside);
_topLevel.Deactivated -= TopLevelDeactivated;
var window = _topLevel as Window;
if (window != null)
window.Deactivated -= WindowDeactivated;
_nonClientListener?.Dispose();
_nonClientListener = null;
}
@ -381,7 +385,7 @@ namespace Avalonia.Controls.Primitives
}
}
private void TopLevelDeactivated(object sender, EventArgs e)
private void WindowDeactivated(object sender, EventArgs e)
{
if (!StaysOpen)
{

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

@ -15,7 +15,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// The root window of a <see cref="Popup"/>.
/// </summary>
public class PopupRoot : TopLevel, IInteractive, IHostedVisualTreeRoot, IDisposable
public class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable
{
private IDisposable _presenterSubscription;

155
src/Avalonia.Controls/TopLevel.cs

@ -18,12 +18,12 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Base class for top-level windows.
/// Base class for top-level widgets.
/// </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="ClientSize"/> and <see cref="IsActive"/> state.
/// This class acts as a base for top level widget.
/// It handles scheduling layout, styling and rendering as well as
/// tracking the widget's <see cref="ClientSize"/>.
/// </remarks>
public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, IRenderRoot, ICloseable, IStyleRoot
{
@ -33,12 +33,6 @@ namespace Avalonia.Controls
public static readonly DirectProperty<TopLevel, Size> ClientSizeProperty =
AvaloniaProperty.RegisterDirect<TopLevel, Size>(nameof(ClientSize), o => o.ClientSize);
/// <summary>
/// Defines the <see cref="IsActive"/> property.
/// </summary>
public static readonly DirectProperty<TopLevel, bool> IsActiveProperty =
AvaloniaProperty.RegisterDirect<TopLevel, bool>(nameof(IsActive), o => o.IsActive);
/// <summary>
/// Defines the <see cref="IInputRoot.PointerOverElement"/> property.
/// </summary>
@ -51,7 +45,6 @@ namespace Avalonia.Controls
private readonly IApplicationLifecycle _applicationLifecycle;
private readonly IPlatformRenderInterface _renderInterface;
private Size _clientSize;
private bool _isActive;
/// <summary>
/// Initializes static members of the <see cref="TopLevel"/> class.
@ -100,21 +93,20 @@ namespace Avalonia.Controls
Renderer = rendererFactory?.CreateRenderer(this, renderLoop);
PlatformImpl.SetInputRoot(this);
PlatformImpl.Activated = HandleActivated;
PlatformImpl.Deactivated = HandleDeactivated;
PlatformImpl.Closed = HandleClosed;
PlatformImpl.Input = HandleInput;
PlatformImpl.Paint = Renderer != null ? (Action<Rect>)Renderer.Render : null;
PlatformImpl.Resized = HandleResized;
PlatformImpl.ScalingChanged = HandleScalingChanged;
PlatformImpl.PositionChanged = HandlePositionChanged;
_keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this);
styler?.ApplyStyles(this);
ClientSize = PlatformImpl.ClientSize;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.ClientSize = x);
this.GetObservable(PointerOverElementProperty)
.Select(
x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
@ -126,26 +118,11 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Fired when the window is activated.
/// </summary>
public event EventHandler Activated;
/// <summary>
/// Fired when the window is closed.
/// </summary>
public event EventHandler Closed;
/// <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;
/// <summary>
/// Gets or sets the client size of the window.
/// </summary>
@ -155,24 +132,6 @@ namespace Avalonia.Controls
private set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); }
}
/// <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>
/// Gets the platform-specific window implementation.
/// </summary>
@ -225,15 +184,6 @@ namespace Avalonia.Controls
get { return AvaloniaLocator.Current.GetService<IGlobalStyles>(); }
}
/// <summary>
/// Whether an auto-size operation is in progress.
/// </summary>
protected bool AutoSizing
{
get;
private set;
}
/// <inheritdoc/>
IRenderTarget IRenderRoot.CreateRenderTarget()
{
@ -258,43 +208,6 @@ namespace Avalonia.Controls
return PlatformImpl.PointToScreen(p);
}
/// <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.ClientSize = finalSize;
}
return base.ArrangeOverride(PlatformImpl.ClientSize);
}
/// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
@ -302,12 +215,6 @@ namespace Avalonia.Controls
/// <param name="clientSize">The new client size.</param>
protected virtual void HandleResized(Size clientSize)
{
if (!AutoSizing)
{
Width = clientSize.Width;
Height = clientSize.Height;
}
ClientSize = clientSize;
LayoutManager.Instance.ExecuteLayoutPass();
PlatformImpl.Invalidate(new Rect(clientSize));
@ -358,23 +265,6 @@ namespace Avalonia.Controls
return result;
}
/// <summary>
/// Handles an activated notification from <see cref="ITopLevelImpl.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 closed notification from <see cref="ITopLevelImpl.Closed"/>.
/// </summary>
@ -398,16 +288,6 @@ namespace Avalonia.Controls
{
}
/// <summary>
/// Handles a deactivated notification from <see cref="ITopLevelImpl.Deactivated"/>.
/// </summary>
private void HandleDeactivated()
{
IsActive = false;
Deactivated?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Handles input from <see cref="ITopLevelImpl.Input"/>.
/// </summary>
@ -416,26 +296,5 @@ namespace Avalonia.Controls
{
_inputManager.ProcessInput(e);
}
/// <summary>
/// Handles a window position change notification from
/// <see cref="ITopLevelImpl.PositionChanged"/>.
/// </summary>
/// <param name="pos">The window position.</param>
private void HandlePositionChanged(Point pos)
{
PositionChanged?.Invoke(this, new PointEventArgs(pos));
}
/// <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);
}
}

2
src/Avalonia.Controls/Window.cs

@ -43,7 +43,7 @@ namespace Avalonia.Controls
/// <summary>
/// A top-level window.
/// </summary>
public class Window : TopLevel, IStyleable, IFocusScope, ILayoutRoot, INameScope
public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
{
private static IList<Window> s_windows = new List<Window>();

191
src/Avalonia.Controls/WindowBase.cs

@ -0,0 +1,191 @@
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="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;
}
base.HandleResized(clientSize);
}
/// <summary>
/// Handles a window position change notification from
/// <see cref="ITopLevelImpl.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="ITopLevelImpl.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="ITopLevelImpl.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);
}
}

3
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -75,8 +75,7 @@ namespace Avalonia.DesignerSupport
private static void SetScalingFactor(double factor)
{
PlatformManager.SetDesignerScalingFactor(factor);
if (s_currentWindow != null)
s_currentWindow.PlatformImpl.ClientSize = s_currentWindow.ClientSize;
s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize);
}
static Window s_currentWindow;

7
src/Gtk/Avalonia.Gtk/EmbeddableImpl.cs

@ -36,15 +36,18 @@ namespace Avalonia.Gtk
public override Size ClientSize
{
get { return new Size(Widget.Allocation.Width, Widget.Allocation.Height); }
set {}
}
//Stubs are needed for future GTK designer embedding support
public override void SetTitle(string title)
{
}
public override void Resize(Size value)
{
}
public override IDisposable ShowDialog() => Disposable.Create(() => { });
public override void SetSystemDecorations(bool enabled)

8
src/Gtk/Avalonia.Gtk/WindowImpl.cs

@ -61,11 +61,11 @@ namespace Avalonia.Gtk
Window.GetSize(out width, out height);
return new Size(width, height);
}
}
set
{
Window.Resize((int)value.Width, (int)value.Height);
}
public override void Resize(Size value)
{
Window.Resize((int)value.Width, (int)value.Height);
}
public override void SetTitle(string title)

6
src/Gtk/Avalonia.Gtk/WindowImplBase.cs

@ -63,8 +63,8 @@ namespace Avalonia.Gtk
_imContext.ClientWindow = _window.GdkWindow;
}
public abstract Size ClientSize { get; set; }
public abstract Size ClientSize { get; }
public abstract void Resize(Size value);
public Size MaxClientSize
{
@ -184,7 +184,7 @@ namespace Avalonia.Gtk
public abstract void BeginResizeDrag(WindowEdge edge);
public abstract Point Position { get; set; }
void ITopLevelImpl.Activate()
void IWindowBaseImpl.Activate()
{
_window.Activate();
}

2
src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj

@ -66,7 +66,7 @@
<Compile Include="PopupImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SystemDialogs.cs" />
<Compile Include="TopLevelImpl.cs" />
<Compile Include="WindowBaseImpl.cs" />
<Compile Include="Interop\Utf8Buffer.cs" />
<Compile Include="WindowImpl.cs" />
</ItemGroup>

4
src/Gtk/Avalonia.Gtk3/FramebufferManager.cs

@ -9,8 +9,8 @@ namespace Avalonia.Gtk3
{
class FramebufferManager : IFramebufferPlatformSurface, IDisposable
{
private readonly TopLevelImpl _window;
public FramebufferManager(TopLevelImpl window)
private readonly WindowBaseImpl _window;
public FramebufferManager(WindowBaseImpl window)
{
_window = window;
}

2
src/Gtk/Avalonia.Gtk3/PopupImpl.cs

@ -9,7 +9,7 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
class PopupImpl : TopLevelImpl, IPopupImpl
class PopupImpl : WindowBaseImpl, IPopupImpl
{
static GtkWindow CreateWindow()
{

4
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@ -77,14 +77,14 @@ namespace Avalonia.Gtk3
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
return ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget,
return ShowDialog(dialog.Title, ((WindowBaseImpl) parent)?.GtkWidget,
dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save,
(dialog as OpenFileDialog)?.AllowMultiple ?? false, dialog.InitialFileName);
}
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
{
var res = await ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget,
var res = await ShowDialog(dialog.Title, ((WindowBaseImpl) parent)?.GtkWidget,
GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory);
return res?.FirstOrDefault();
}

17
src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs → src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -12,7 +12,7 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle
abstract class WindowBaseImpl : IWindowBaseImpl, IPlatformHandle
{
public readonly GtkWindow GtkWidget;
private IInputRoot _inputRoot;
@ -25,7 +25,7 @@ namespace Avalonia.Gtk3
private uint _lastKbdEvent;
private uint _lastSmoothScrollEvent;
public TopLevelImpl(GtkWindow gtkWidget)
public WindowBaseImpl(GtkWindow gtkWidget)
{
GtkWidget = gtkWidget;
@ -318,12 +318,13 @@ namespace Avalonia.Gtk3
Native.GtkWindowGetSize(GtkWidget, out w, out h);
return new Size(w, h);
}
set
{
if (GtkWidget.IsClosed)
return;
Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
}
}
public void Resize(Size value)
{
if (GtkWidget.IsClosed)
return;
Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
}
public Point Position

2
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@ -5,7 +5,7 @@ using Avalonia.Platform;
namespace Avalonia.Gtk3
{
class WindowImpl : TopLevelImpl, IWindowImpl
class WindowImpl : WindowBaseImpl, IWindowImpl
{
public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel))
{

10
src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs

@ -15,10 +15,12 @@ namespace Avalonia.Win32.Embedding
{
private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot();
private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle;
public WinFormsAvaloniaControlHost()
{
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
UnmanagedMethods.SetParent(_root.PlatformImpl.Handle.Handle, Handle);
UnmanagedMethods.SetParent(WindowHandle, Handle);
_root.Prepare();
if (_root.IsFocused)
FocusManager.Instance.Focus(null);
@ -59,20 +61,20 @@ namespace Avalonia.Win32.Embedding
private void RootGotFocus(object sender, Interactivity.RoutedEventArgs e)
{
UnmanagedMethods.SetFocus(_root.PlatformImpl.Handle.Handle);
UnmanagedMethods.SetFocus(WindowHandle);
}
protected override void OnGotFocus(EventArgs e)
{
if (_root != null)
UnmanagedMethods.SetFocus(_root.PlatformImpl.Handle.Handle);
UnmanagedMethods.SetFocus(WindowHandle);
}
void FixPosition()
{
if (_root != null && Width > 0 && Height > 0)
UnmanagedMethods.MoveWindow(_root.PlatformImpl.Handle.Handle, 0, 0, Width, Height, true);
UnmanagedMethods.MoveWindow(WindowHandle, 0, 0, Width, Height, true);
}

4
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -187,7 +187,9 @@ namespace Avalonia.Win32
#if NETSTANDARD
throw new NotSupportedException();
#else
return new EmbeddedWindowImpl();
var embedded = new EmbeddedWindowImpl();
embedded.Show();
return embedded;
#endif
}

30
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -89,23 +89,23 @@ namespace Avalonia.Win32
UnmanagedMethods.GetClientRect(_hwnd, out rect);
return new Size(rect.right, rect.bottom) / Scaling;
}
}
set
public void Resize(Size value)
{
if (value != ClientSize)
{
if (value != ClientSize)
{
value *= Scaling;
value += BorderThickness;
UnmanagedMethods.SetWindowPos(
_hwnd,
IntPtr.Zero,
0,
0,
(int)value.Width,
(int)value.Height,
UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
}
value *= Scaling;
value += BorderThickness;
UnmanagedMethods.SetWindowPos(
_hwnd,
IntPtr.Zero,
0,
0,
(int)value.Width,
(int)value.Height,
UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
}
}

Loading…
Cancel
Save