From 0af6dd2c0a0361012220bcccb9febddd37d4531a Mon Sep 17 00:00:00 2001 From: CommonGuy Date: Mon, 4 Jun 2018 19:55:31 +0200 Subject: [PATCH 1/5] Implement topmost on window --- src/Avalonia.Controls/Platform/IWindowImpl.cs | 5 +++++ src/Avalonia.Controls/Window.cs | 14 ++++++++++++++ .../Remote/PreviewerWindowImpl.cs | 4 ++++ src/Avalonia.DesignerSupport/Remote/Stubs.cs | 4 ++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 6 +++++- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 4 +++- src/OSX/Avalonia.MonoMac/WindowImpl.cs | 5 +++++ .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 8 ++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 17 +++++++++++++++++ 9 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index f1f3925133..093e9bc57c 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -55,6 +55,11 @@ namespace Avalonia.Platform /// void CanResize(bool value); + /// + /// Gets or sets whether this window appears on top of all other windows + /// + void SetTopmost(bool value); + /// /// Gets or sets a method called before the underlying implementation is destroyed. /// Return true to prevent the underlying implementation from closing. diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 3cbfdbd657..1943225b00 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -104,6 +104,9 @@ namespace Avalonia.Controls public static readonly StyledProperty CanResizeProperty = AvaloniaProperty.Register(nameof(CanResize), true); + public static readonly StyledProperty TopmostProperty = + AvaloniaProperty.Register(nameof(Topmost)); + private readonly NameScope _nameScope = new NameScope(); private object _dialogResult; private readonly Size _maxPlatformClientSize; @@ -125,6 +128,8 @@ namespace Avalonia.Controls CanResizeProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue)); + TopmostProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetTopmost((bool)e.NewValue)); + WindowStateProperty.Changed.AddClassHandler( (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; }); } @@ -230,6 +235,15 @@ namespace Avalonia.Controls set { SetValue(CanResizeProperty, value); } } + /// + /// Gets or sets whether this window appears on top of all other windows + /// + public bool Topmost + { + get { return GetValue(TopmostProperty); } + set { SetValue(TopmostProperty, value); } + } + /// /// Gets or sets the icon of the window. /// diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index ef16d06b60..d9a30f0d29 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -102,5 +102,9 @@ namespace Avalonia.DesignerSupport.Remote public void CanResize(bool value) { } + + public void SetTopmost(bool value) + { + } } } \ No newline at end of file diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index e749d10468..43f0e0a95e 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/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 diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 1adaf9f4e1..9cc6ba6901 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/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; diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index bae34db6f3..4756d33e0f 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -81,7 +81,9 @@ namespace Avalonia.Gtk3 public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value); public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value); - + + public void SetTopmost(bool value) => Native.GtkWindowSetKeepAbove(GtkWidget, value); + class EmptyDisposable : IDisposable { diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs index a2f8df6791..34a0702f8c 100644 --- a/src/OSX/Avalonia.MonoMac/WindowImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -116,6 +116,11 @@ namespace Avalonia.MonoMac UpdateStyle(); } + public void SetTopmost(bool value) + { + Window.Level = value ? NSWindowLevel.Floating : NSWindowLevel.Normal; + } + public void SetTitle(string title) => Window.Title = title; class ModalDisposable : IDisposable diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 86dcec410b..daaee9636e 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/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, diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 7b9f8ee066..8c0af62c74 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/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; @@ -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; + } } } From c19971a44f49e0b2aa4d52b166d69aac956422c7 Mon Sep 17 00:00:00 2001 From: CommonGuy Date: Mon, 4 Jun 2018 19:58:53 +0200 Subject: [PATCH 2/5] Replace relevant WindowPos magic number with variable --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 8c0af62c74..97e5aa99ec 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -839,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); } } } From 42121ec2c15c755f113edabc31da92e4307b5578 Mon Sep 17 00:00:00 2001 From: CommonGuy Date: Fri, 8 Jun 2018 08:05:28 +0200 Subject: [PATCH 3/5] Move topmost to WindowBase --- src/Avalonia.Controls/Platform/IWindowBaseImpl.cs | 5 +++++ src/Avalonia.Controls/Platform/IWindowImpl.cs | 5 ----- src/Avalonia.Controls/Window.cs | 14 -------------- src/Avalonia.Controls/WindowBase.cs | 14 ++++++++++++++ src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs | 2 ++ src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 2 -- src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs | 1 + src/OSX/Avalonia.MonoMac/WindowImpl.cs | 5 ----- 8 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 4f7ac82df7..9ba68f584e 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -72,6 +72,11 @@ namespace Avalonia.Platform /// void SetMinMaxSize(Size minSize, Size maxSize); + /// + /// Sets whether this window appears on top of all other windows + /// + void SetTopmost(bool value); + /// /// Gets platform specific display information /// diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index 093e9bc57c..f1f3925133 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -55,11 +55,6 @@ namespace Avalonia.Platform /// void CanResize(bool value); - /// - /// Gets or sets whether this window appears on top of all other windows - /// - void SetTopmost(bool value); - /// /// Gets or sets a method called before the underlying implementation is destroyed. /// Return true to prevent the underlying implementation from closing. diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 1943225b00..3cbfdbd657 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -104,9 +104,6 @@ namespace Avalonia.Controls public static readonly StyledProperty CanResizeProperty = AvaloniaProperty.Register(nameof(CanResize), true); - public static readonly StyledProperty TopmostProperty = - AvaloniaProperty.Register(nameof(Topmost)); - private readonly NameScope _nameScope = new NameScope(); private object _dialogResult; private readonly Size _maxPlatformClientSize; @@ -128,8 +125,6 @@ namespace Avalonia.Controls CanResizeProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue)); - TopmostProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetTopmost((bool)e.NewValue)); - WindowStateProperty.Changed.AddClassHandler( (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; }); } @@ -235,15 +230,6 @@ namespace Avalonia.Controls set { SetValue(CanResizeProperty, value); } } - /// - /// Gets or sets whether this window appears on top of all other windows - /// - public bool Topmost - { - get { return GetValue(TopmostProperty); } - set { SetValue(TopmostProperty, value); } - } - /// /// Gets or sets the icon of the window. /// diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index c427df1c26..5d66bee2f8 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -38,6 +38,9 @@ namespace Avalonia.Controls o => o.Owner, (o, v) => o.Owner = v); + public static readonly StyledProperty TopmostProperty = + AvaloniaProperty.Register(nameof(Topmost)); + private bool _hasExecutedInitialLayoutPass; private bool _isActive; private bool _ignoreVisibilityChange; @@ -52,6 +55,8 @@ namespace Avalonia.Controls MinHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight))); MaxWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight))); MaxHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue))); + + TopmostProperty.Changed.AddClassHandler((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); } } + /// + /// Gets or sets whether this window appears on top of all other windows + /// + public bool Topmost + { + get { return GetValue(TopmostProperty); } + set { SetValue(TopmostProperty, value); } + } + /// /// Activates the window. /// diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs index 94537d3475..8a880fd306 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs +++ b/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; diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index 4756d33e0f..a0b754c229 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -82,8 +82,6 @@ namespace Avalonia.Gtk3 public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value); - public void SetTopmost(bool value) => Native.GtkWindowSetKeepAbove(GtkWidget, value); - class EmptyDisposable : IDisposable { diff --git a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs index 8cbc6cbdd8..89cef59b53 100644 --- a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs +++ b/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() { diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs index 34a0702f8c..a2f8df6791 100644 --- a/src/OSX/Avalonia.MonoMac/WindowImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -116,11 +116,6 @@ namespace Avalonia.MonoMac UpdateStyle(); } - public void SetTopmost(bool value) - { - Window.Level = value ? NSWindowLevel.Floating : NSWindowLevel.Normal; - } - public void SetTitle(string title) => Window.Title = title; class ModalDisposable : IDisposable From 835fbe251c0ddf8cd9da75ed2d377e2575e8836a Mon Sep 17 00:00:00 2001 From: CommonGuy Date: Fri, 8 Jun 2018 09:47:47 +0200 Subject: [PATCH 4/5] Implement Topmost property on Popup --- src/Avalonia.Controls/Primitives/Popup.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 656f3890cd..005717d681 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -70,6 +70,12 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty StaysOpenProperty = AvaloniaProperty.Register(nameof(StaysOpen), true); + /// + /// Defines the property. + /// + public static readonly StyledProperty TopmostProperty = + AvaloniaProperty.Register(nameof(Topmost)); + private bool _isOpen; private PopupRoot _popupRoot; private TopLevel _topLevel; @@ -84,6 +90,7 @@ namespace Avalonia.Controls.Primitives IsHitTestVisibleProperty.OverrideDefaultValue(false); ChildProperty.Changed.AddClassHandler(x => x.ChildChanged); IsOpenProperty.Changed.AddClassHandler(x => x.IsOpenChanged); + TopmostProperty.Changed.AddClassHandler((p, e) => p.PopupRoot.Topmost = (bool)e.NewValue); } /// @@ -194,6 +201,15 @@ namespace Avalonia.Controls.Primitives set { SetValue(StaysOpenProperty, value); } } + /// + /// Gets or sets whether this popup appears on top of all other windows + /// + public bool Topmost + { + get { return GetValue(TopmostProperty); } + set { SetValue(TopmostProperty, value); } + } + /// /// Gets the root of the popup window. /// From ffbc89f50ca1b0dafd72556d91c8ce930cdda151 Mon Sep 17 00:00:00 2001 From: CommonGuy Date: Wed, 13 Jun 2018 08:30:24 +0200 Subject: [PATCH 5/5] Implement SetTopmost stub for Android --- .../Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs index 78f744cea0..041d91043a 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs +++ b/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 + } } } \ No newline at end of file