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
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/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.
///
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/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/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 bae34db6f3..a0b754c229 100644
--- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs
+++ b/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
{
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/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..97e5aa99ec 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;
@@ -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;
+ }
}
}