Răsfoiți Sursa

Merge pull request #1647 from CommonGuy/window-topmost

Implement Topmost on Window
Jumar Macato 7 ani în urmă
părinte
comite
c2a113d693

+ 4 - 1
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@@ -110,7 +110,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         {
             //Not supported
         }
-        
 
+        public void SetTopmost(bool value)
+        {
+            //Not supported
+        }
     }
 }

+ 5 - 0
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@@ -72,6 +72,11 @@ namespace Avalonia.Platform
         /// 
         void SetMinMaxSize(Size minSize, Size maxSize);
 
+        /// <summary>
+        /// Sets whether this window appears on top of all other windows
+        /// </summary>
+        void SetTopmost(bool value);
+
         /// <summary>
         /// Gets platform specific display information
         /// </summary>

+ 16 - 0
src/Avalonia.Controls/Primitives/Popup.cs

@@ -70,6 +70,12 @@ namespace Avalonia.Controls.Primitives
         public static readonly StyledProperty<bool> StaysOpenProperty =
             AvaloniaProperty.Register<Popup, bool>(nameof(StaysOpen), true);
 
+        /// <summary>
+        /// Defines the <see cref="Topmost"/> property.
+        /// </summary>
+        public static readonly StyledProperty<bool> TopmostProperty =
+            AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
+
         private bool _isOpen;
         private PopupRoot _popupRoot;
         private TopLevel _topLevel;
@@ -84,6 +90,7 @@ namespace Avalonia.Controls.Primitives
             IsHitTestVisibleProperty.OverrideDefaultValue<Popup>(false);
             ChildProperty.Changed.AddClassHandler<Popup>(x => x.ChildChanged);
             IsOpenProperty.Changed.AddClassHandler<Popup>(x => x.IsOpenChanged);
+            TopmostProperty.Changed.AddClassHandler<Popup>((p, e) => p.PopupRoot.Topmost = (bool)e.NewValue);
         }
 
         /// <summary>
@@ -194,6 +201,15 @@ namespace Avalonia.Controls.Primitives
             set { SetValue(StaysOpenProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets whether this popup appears on top of all other windows
+        /// </summary>
+        public bool Topmost
+        {
+            get { return GetValue(TopmostProperty); }
+            set { SetValue(TopmostProperty, value); }
+        }
+
         /// <summary>
         /// Gets the root of the popup window.
         /// </summary>

+ 14 - 0
src/Avalonia.Controls/WindowBase.cs

@@ -38,6 +38,9 @@ namespace Avalonia.Controls
                 o => o.Owner,
                 (o, v) => o.Owner = v);
 
+        public static readonly StyledProperty<bool> TopmostProperty =
+            AvaloniaProperty.Register<WindowBase, bool>(nameof(Topmost));
+
         private bool _hasExecutedInitialLayoutPass;
         private bool _isActive;
         private bool _ignoreVisibilityChange;
@@ -52,6 +55,8 @@ namespace Avalonia.Controls
             MinHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight)));
             MaxWidthProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight)));
             MaxHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue)));
+            
+            TopmostProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetTopmost((bool)e.NewValue));
         }
 
         public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
@@ -124,6 +129,15 @@ namespace Avalonia.Controls
             set { SetAndRaise(OwnerProperty, ref _owner, value); }
         }
 
+        /// <summary>
+        /// Gets or sets whether this window appears on top of all other windows
+        /// </summary>
+        public bool Topmost
+        {
+            get { return GetValue(TopmostProperty); }
+            set { SetValue(TopmostProperty, value); }
+        }
+
         /// <summary>
         /// Activates the window.
         /// </summary>

+ 4 - 0
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@@ -102,5 +102,9 @@ namespace Avalonia.DesignerSupport.Remote
         public void CanResize(bool value)
         {
         }
+
+        public void SetTopmost(bool value)
+        {
+        }
     }
 }

+ 4 - 0
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -104,6 +104,10 @@ namespace Avalonia.DesignerSupport.Remote
         public void CanResize(bool value)
         {
         }
+
+        public void SetTopmost(bool value)
+        {
+        }
     }
 
     class ClipboardStub : IClipboard

+ 5 - 1
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@@ -261,10 +261,13 @@ namespace Avalonia.Gtk3.Interop
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_unmaximize(GtkWindow window);
-            
+
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_close(GtkWindow window);
 
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_window_set_keep_above(GtkWindow gtkWindow, bool setting);
+
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask);
 
@@ -472,6 +475,7 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_window_maximize GtkWindowMaximize;
         public static D.gtk_window_unmaximize GtkWindowUnmaximize;
         public static D.gtk_window_close GtkWindowClose;
+        public static D.gtk_window_set_keep_above GtkWindowSetKeepAbove;
         public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag;
         public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
         public static D.gdk_event_request_motions GdkEventRequestMotions;

+ 2 - 0
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -416,6 +416,8 @@ namespace Avalonia.Gtk3
 
         public void Hide() => Native.GtkWidgetHide(GtkWidget);
 
+        public void SetTopmost(bool value) => Native.GtkWindowSetKeepAbove(GtkWidget, value);
+
         void GetGlobalPointer(out int x, out int y)
         {
             int mask;

+ 1 - 1
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@@ -81,7 +81,7 @@ namespace Avalonia.Gtk3
         public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value);
 
         public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value);
-        
+
 
         class EmptyDisposable : IDisposable
         {

+ 1 - 0
src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs

@@ -123,6 +123,7 @@ namespace Avalonia.MonoMac
 
         public void Hide() => Window?.OrderOut(Window);
 
+        public void SetTopmost(bool value) => Window.Level = value ? NSWindowLevel.Floating : NSWindowLevel.Normal;
 
         public void BeginMoveDrag()
         {

+ 8 - 0
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -78,6 +78,14 @@ namespace Avalonia.Win32.Interop
             SWP_RESIZE = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER
         }
 
+        public static class WindowPosZOrder
+        {
+            public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
+            public static readonly IntPtr HWND_TOP = new IntPtr(0);
+            public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
+            public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
+        }
+
         public enum SizeCommand
         {
             Restored,

+ 18 - 1
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -32,6 +32,7 @@ namespace Avalonia.Win32
         private bool _trackingMouse;
         private bool _decorated = true;
         private bool _resizable = true;
+        private bool _topmost = false;
         private double _scaling = 1;
         private WindowState _showWindowState;
         private WindowState _lastWindowState;
@@ -838,7 +839,7 @@ namespace Avalonia.Win32
                     var cx = Math.Abs(monitorInfo.rcWork.right - x);
                     var cy = Math.Abs(monitorInfo.rcWork.bottom - y);
 
-                    SetWindowPos(_hwnd, new IntPtr(-2), x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
+                    SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
                 }
             }
         }
@@ -901,5 +902,21 @@ namespace Avalonia.Win32
 
             _resizable = value;
         }
+
+        public void SetTopmost(bool value)
+        {
+            if (value == _topmost)
+            {
+                return;
+            }
+
+            IntPtr hWndInsertAfter = value ? WindowPosZOrder.HWND_TOPMOST : WindowPosZOrder.HWND_NOTOPMOST;
+            UnmanagedMethods.SetWindowPos(_hwnd,
+                   hWndInsertAfter,
+                   0, 0, 0, 0,
+                   SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
+
+            _topmost = value;
+        }
     }
 }