浏览代码

Refactored out WindowBase from TopLevel

Nikita Tsukanov 8 年之前
父节点
当前提交
f21212e76a
共有 31 个文件被更改,包括 377 次插入295 次删除
  1. 3 3
      Avalonia.sln
  2. 7 9
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  3. 1 3
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  4. 2 0
      src/Avalonia.Controls/Avalonia.Controls.csproj
  5. 1 3
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  6. 9 3
      src/Avalonia.Controls/Menu.cs
  7. 1 1
      src/Avalonia.Controls/Platform/IEmbeddableWindowImpl.cs
  8. 1 1
      src/Avalonia.Controls/Platform/IPopupImpl.cs
  9. 12 66
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  10. 69 0
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  11. 1 1
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  12. 1 1
      src/Avalonia.Controls/Platform/PlatformManager.cs
  13. 9 5
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  14. 7 3
      src/Avalonia.Controls/Primitives/Popup.cs
  15. 1 1
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  16. 7 148
      src/Avalonia.Controls/TopLevel.cs
  17. 1 1
      src/Avalonia.Controls/Window.cs
  18. 191 0
      src/Avalonia.Controls/WindowBase.cs
  19. 1 2
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  20. 5 2
      src/Gtk/Avalonia.Gtk/EmbeddableImpl.cs
  21. 4 4
      src/Gtk/Avalonia.Gtk/WindowImpl.cs
  22. 3 3
      src/Gtk/Avalonia.Gtk/WindowImplBase.cs
  23. 1 1
      src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
  24. 2 2
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  25. 1 1
      src/Gtk/Avalonia.Gtk3/PopupImpl.cs
  26. 2 2
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  27. 9 8
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  28. 1 1
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  29. 6 4
      src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
  30. 3 1
      src/Windows/Avalonia.Win32/Win32Platform.cs
  31. 15 15
      src/Windows/Avalonia.Win32/WindowImpl.cs

+ 3 - 3
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

+ 7 - 9
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

+ 1 - 3
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 - 0
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" />

+ 1 - 3
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);
         }
 

+ 9 - 3
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;

+ 1 - 1
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;
     }

+ 1 - 1
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
     {
 
     }

+ 12 - 66
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 - 0
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);
+    }
+}

+ 1 - 1
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.

+ 1 - 1
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()

+ 9 - 5
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);
                     }
                 }
 

+ 7 - 3
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)
             {

+ 1 - 1
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;
 

+ 7 - 148
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);
     }
 }

+ 1 - 1
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 - 0
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);
+    }
+}

+ 1 - 2
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;

+ 5 - 2
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)

+ 4 - 4
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)

+ 3 - 3
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();
         }

+ 1 - 1
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>

+ 2 - 2
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;
         }

+ 1 - 1
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()
         {

+ 2 - 2
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();
         }

+ 9 - 8
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

+ 1 - 1
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))
         {

+ 6 - 4
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);
         }
 
 

+ 3 - 1
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
         }
 

+ 15 - 15
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);
             }
         }