Add Window::set_window_level API

This adds `Window::set_window_level` to control the preferred
z level of the window.

Co-authored-by: Markus Siglreithmaier <m.siglreith@gmail.com>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
Co-authored-by: Mads Marquart <mads@marquart.dk>
This commit is contained in:
Amr Bashir 2022-11-26 03:50:58 +02:00 committed by GitHub
parent ba4bf03675
commit 101ac8908c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 142 additions and 82 deletions

View file

@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased # Unreleased
- **Breaking:** Removed `Window::set_always_on_top` and related APIs in favor of `Window::set_window_level`.
- On Windows, MacOS and X11, add always on bottom APIs.
- On Windows, fix the value in `MouseButton::Other`. - On Windows, fix the value in `MouseButton::Other`.
- On macOS, add `WindowExtMacOS::is_document_edited` and `WindowExtMacOS::set_document_edited` APIs. - On macOS, add `WindowExtMacOS::is_document_edited` and `WindowExtMacOS::set_document_edited` APIs.
- **Breaking:** Removed `WindowBuilderExtIOS::with_root_view_class`; instead, you should use `[[view layer] addSublayer: ...]` to add an instance of the desired layer class (e.g. `CAEAGLLayer` or `CAMetalLayer`). See `vulkano-win` or `wgpu` for examples of this. - **Breaking:** Removed `WindowBuilderExtIOS::with_root_view_class`; instead, you should use `[[view layer] addSublayer: ...]` to add an instance of the desired layer class (e.g. `CAEAGLLayer` or `CAMetalLayer`). See `vulkano-win` or `wgpu` for examples of this.

View file

@ -9,7 +9,7 @@ fn main() {
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder}, window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
}; };
const WINDOW_COUNT: usize = 3; const WINDOW_COUNT: usize = 3;
@ -66,7 +66,9 @@ fn main() {
let state = !modifiers.shift(); let state = !modifiers.shift();
use VirtualKeyCode::*; use VirtualKeyCode::*;
match key { match key {
A => window.set_always_on_top(state), Key1 => window.set_window_level(WindowLevel::AlwaysOnTop),
Key2 => window.set_window_level(WindowLevel::AlwaysOnBottom),
Key3 => window.set_window_level(WindowLevel::Normal),
C => window.set_cursor_icon(match state { C => window.set_cursor_icon(match state {
true => CursorIcon::Progress, true => CursorIcon::Progress,
false => CursorIcon::Default, false => CursorIcon::Default,

View file

@ -24,7 +24,7 @@ use crate::{
error, error,
event::{self, StartCause, VirtualKeyCode}, event::{self, StartCause, VirtualKeyCode},
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW}, event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
window::{self, CursorGrabMode, Theme}, window::{self, CursorGrabMode, Theme, WindowLevel},
}; };
fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option<event::VirtualKeyCode> { fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option<event::VirtualKeyCode> {
@ -981,7 +981,7 @@ impl Window {
true true
} }
pub fn set_always_on_top(&self, _always_on_top: bool) {} pub fn set_window_level(&self, _level: WindowLevel) {}
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {} pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}

View file

@ -24,7 +24,7 @@ use crate::{
}, },
window::{ window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes,
WindowId as RootWindowId, WindowId as RootWindowId, WindowLevel,
}, },
}; };
@ -282,8 +282,8 @@ impl Inner {
true true
} }
pub fn set_always_on_top(&self, _always_on_top: bool) { pub fn set_window_level(&self, _level: WindowLevel) {
warn!("`Window::set_always_on_top` is ignored on iOS") warn!("`Window::set_window_level` is ignored on iOS")
} }
pub fn set_window_icon(&self, _icon: Option<Icon>) { pub fn set_window_icon(&self, _icon: Option<Icon>) {
@ -395,9 +395,7 @@ impl Window {
if window_attributes.max_inner_size.is_some() { if window_attributes.max_inner_size.is_some() {
warn!("`WindowAttributes::max_inner_size` is ignored on iOS"); warn!("`WindowAttributes::max_inner_size` is ignored on iOS");
} }
if window_attributes.always_on_top {
warn!("`WindowAttributes::always_on_top` is unsupported on iOS");
}
// TODO: transparency, visible // TODO: transparency, visible
unsafe { unsafe {

View file

@ -39,7 +39,7 @@ use crate::{
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW, ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
}, },
icon::Icon, icon::Icon,
window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes}, window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowLevel},
}; };
pub(crate) use crate::icon::RgbaIcon as PlatformIcon; pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
@ -476,10 +476,10 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_always_on_top(&self, _always_on_top: bool) { pub fn set_window_level(&self, _level: WindowLevel) {
match self { match self {
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
Window::X(ref w) => w.set_always_on_top(_always_on_top), Window::X(ref w) => w.set_window_level(_level),
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Window::Wayland(_) => (), Window::Wayland(_) => (),
} }

View file

@ -20,7 +20,9 @@ use crate::{
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
}, },
window::{CursorGrabMode, CursorIcon, Icon, Theme, UserAttentionType, WindowAttributes}, window::{
CursorGrabMode, CursorIcon, Icon, Theme, UserAttentionType, WindowAttributes, WindowLevel,
},
}; };
use super::{ use super::{
@ -490,12 +492,11 @@ impl UnownedWindow {
shared_state.restore_position = Some((x, y)); shared_state.restore_position = Some((x, y));
} }
} }
if window_attrs.always_on_top {
window window
.set_always_on_top_inner(window_attrs.always_on_top) .set_window_level_inner(window_attrs.window_level)
.queue(); .queue();
} }
}
// We never want to give the user a broken window, since by then, it's too late to handle. // We never want to give the user a broken window, since by then, it's too late to handle.
xconn xconn
@ -919,16 +920,25 @@ impl UnownedWindow {
self.xconn.set_motif_hints(self.xwindow, &hints) self.xconn.set_motif_hints(self.xwindow, &hints)
} }
fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher<'_> { fn toggle_atom(&self, atom_bytes: &[u8], enable: bool) -> util::Flusher<'_> {
let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") }; let atom = unsafe { self.xconn.get_atom_unchecked(atom_bytes) };
self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0)) self.set_netwm(enable.into(), (atom as c_long, 0, 0, 0))
}
fn set_window_level_inner(&self, level: WindowLevel) -> util::Flusher<'_> {
self.toggle_atom(b"_NET_WM_STATE_ABOVE\0", level == WindowLevel::AlwaysOnTop)
.queue();
self.toggle_atom(
b"_NET_WM_STATE_BELOW\0",
level == WindowLevel::AlwaysOnBottom,
)
} }
#[inline] #[inline]
pub fn set_always_on_top(&self, always_on_top: bool) { pub fn set_window_level(&self, level: WindowLevel) {
self.set_always_on_top_inner(always_on_top) self.set_window_level_inner(level)
.flush() .flush()
.expect("Failed to set always-on-top state"); .expect("Failed to set window-level state");
} }
fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> { fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> {

View file

@ -296,6 +296,8 @@ pub struct NSWindowLevel(pub NSInteger);
#[allow(dead_code)] #[allow(dead_code)]
impl NSWindowLevel { impl NSWindowLevel {
#[doc(alias = "BelowNormalWindowLevel")]
pub const BELOW_NORMAL: Self = Self((kCGNormalWindowLevel - 1) as _);
#[doc(alias = "NSNormalWindowLevel")] #[doc(alias = "NSNormalWindowLevel")]
pub const Normal: Self = Self(kCGNormalWindowLevel as _); pub const Normal: Self = Self(kCGNormalWindowLevel as _);
#[doc(alias = "NSFloatingWindowLevel")] #[doc(alias = "NSFloatingWindowLevel")]

View file

@ -30,7 +30,7 @@ use crate::{
}, },
window::{ window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes,
WindowId as RootWindowId, WindowId as RootWindowId, WindowLevel,
}, },
}; };
use core_graphics::display::{CGDisplay, CGPoint}; use core_graphics::display::{CGDisplay, CGPoint};
@ -333,10 +333,6 @@ impl WinitWindow {
this.setMovableByWindowBackground(true); this.setMovableByWindowBackground(true);
} }
if attrs.always_on_top {
this.setLevel(NSWindowLevel::Floating);
}
if let Some(increments) = attrs.resize_increments { if let Some(increments) = attrs.resize_increments {
let increments = increments.to_logical(this.scale_factor()); let increments = increments.to_logical(this.scale_factor());
let (w, h) = (increments.width, increments.height); let (w, h) = (increments.width, increments.height);
@ -394,6 +390,8 @@ impl WinitWindow {
this.set_max_inner_size(Some(dim)); this.set_max_inner_size(Some(dim));
} }
this.set_window_level(attrs.window_level);
// register for drag and drop operations. // register for drag and drop operations.
this.registerForDraggedTypes(&NSArray::from_slice(&[ this.registerForDraggedTypes(&NSArray::from_slice(&[
unsafe { NSFilenamesPboardType }.copy() unsafe { NSFilenamesPboardType }.copy()
@ -1019,11 +1017,11 @@ impl WinitWindow {
} }
#[inline] #[inline]
pub fn set_always_on_top(&self, always_on_top: bool) { pub fn set_window_level(&self, level: WindowLevel) {
let level = if always_on_top { let level = match level {
NSWindowLevel::Floating WindowLevel::AlwaysOnTop => NSWindowLevel::Floating,
} else { WindowLevel::AlwaysOnBottom => NSWindowLevel::BELOW_NORMAL,
NSWindowLevel::Normal WindowLevel::Normal => NSWindowLevel::Normal,
}; };
util::set_level_async(self, level); util::set_level_async(self, level);
} }

View file

@ -4,6 +4,7 @@ use crate::event;
use crate::icon::Icon; use crate::icon::Icon;
use crate::window::{ use crate::window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowId as RootWI, CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowId as RootWI,
WindowLevel,
}; };
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle};
@ -307,7 +308,7 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_always_on_top(&self, _always_on_top: bool) { pub fn set_window_level(&self, _level: WindowLevel) {
// Intentionally a no-op, no window ordering // Intentionally a no-op, no window ordering
} }

View file

@ -71,7 +71,7 @@ use crate::{
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
Fullscreen, Parent, PlatformSpecificWindowBuilderAttributes, WindowId, Fullscreen, Parent, PlatformSpecificWindowBuilderAttributes, WindowId,
}, },
window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes}, window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowLevel},
}; };
/// The Win32 implementation of the main `Window` object. /// The Win32 implementation of the main `Window` object.
@ -605,14 +605,21 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_always_on_top(&self, always_on_top: bool) { pub fn set_window_level(&self, level: WindowLevel) {
let window = self.window.clone(); let window = self.window.clone();
let window_state = Arc::clone(&self.window_state); let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || { self.thread_executor.execute_in_thread(move || {
let _ = &window; let _ = &window;
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| { WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top) f.set(
WindowFlags::ALWAYS_ON_TOP,
level == WindowLevel::AlwaysOnTop,
);
f.set(
WindowFlags::ALWAYS_ON_BOTTOM,
level == WindowLevel::AlwaysOnBottom,
);
}); });
}); });
} }
@ -985,7 +992,14 @@ where
WindowFlags::MARKER_UNDECORATED_SHADOW, WindowFlags::MARKER_UNDECORATED_SHADOW,
pl_attribs.decoration_shadow, pl_attribs.decoration_shadow,
); );
window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top); window_flags.set(
WindowFlags::ALWAYS_ON_TOP,
attributes.window_level == WindowLevel::AlwaysOnTop,
);
window_flags.set(
WindowFlags::ALWAYS_ON_BOTTOM,
attributes.window_level == WindowLevel::AlwaysOnBottom,
);
window_flags.set( window_flags.set(
WindowFlags::NO_BACK_BUFFER, WindowFlags::NO_BACK_BUFFER,
pl_attribs.no_redirection_bitmap, pl_attribs.no_redirection_bitmap,

View file

@ -12,14 +12,14 @@ use windows_sys::Win32::{
Graphics::Gdi::InvalidateRgn, Graphics::Gdi::InvalidateRgn,
UI::WindowsAndMessaging::{ UI::WindowsAndMessaging::{
AdjustWindowRectEx, GetMenu, GetWindowLongW, SendMessageW, SetWindowLongW, SetWindowPos, AdjustWindowRectEx, GetMenu, GetWindowLongW, SendMessageW, SetWindowLongW, SetWindowPos,
ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_NOTOPMOST, HWND_TOPMOST, SWP_ASYNCWINDOWPOS, ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_BOTTOM, HWND_NOTOPMOST, HWND_TOPMOST,
SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOREPOSITION, SWP_NOSIZE, SWP_NOZORDER, SWP_ASYNCWINDOWPOS, SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOREPOSITION,
SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW, WINDOWPLACEMENT, WINDOW_EX_STYLE, SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW,
WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD, WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WINDOWPLACEMENT, WINDOW_EX_STYLE, WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD,
WS_EX_ACCEPTFILES, WS_EX_APPWINDOW, WS_EX_LAYERED, WS_EX_NOREDIRECTIONBITMAP, WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WS_EX_ACCEPTFILES, WS_EX_APPWINDOW, WS_EX_LAYERED,
WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_EX_WINDOWEDGE, WS_MAXIMIZE, WS_MAXIMIZEBOX, WS_EX_NOREDIRECTIONBITMAP, WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_EX_WINDOWEDGE, WS_MAXIMIZE,
WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SIZEBOX, WS_SYSMENU, WS_MAXIMIZEBOX, WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SIZEBOX,
WS_VISIBLE, WS_SYSMENU, WS_VISIBLE,
}, },
}; };
@ -80,34 +80,35 @@ bitflags! {
const VISIBLE = 1 << 1; const VISIBLE = 1 << 1;
const ON_TASKBAR = 1 << 2; const ON_TASKBAR = 1 << 2;
const ALWAYS_ON_TOP = 1 << 3; const ALWAYS_ON_TOP = 1 << 3;
const NO_BACK_BUFFER = 1 << 4; const ALWAYS_ON_BOTTOM = 1 << 4;
const TRANSPARENT = 1 << 5; const NO_BACK_BUFFER = 1 << 5;
const CHILD = 1 << 6; const TRANSPARENT = 1 << 6;
const MAXIMIZED = 1 << 7; const CHILD = 1 << 7;
const POPUP = 1 << 8; const MAXIMIZED = 1 << 8;
const POPUP = 1 << 9;
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is /// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
/// included here to make masking easier. /// included here to make masking easier.
const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 9; const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 10;
const MARKER_BORDERLESS_FULLSCREEN = 1 << 10; const MARKER_BORDERLESS_FULLSCREEN = 1 << 11;
/// The `WM_SIZE` event contains some parameters that can effect the state of `WindowFlags`. /// The `WM_SIZE` event contains some parameters that can effect the state of `WindowFlags`.
/// In most cases, it's okay to let those parameters change the state. However, when we're /// In most cases, it's okay to let those parameters change the state. However, when we're
/// running the `WindowFlags::apply_diff` function, we *don't* want those parameters to /// running the `WindowFlags::apply_diff` function, we *don't* want those parameters to
/// effect our stored state, because the purpose of `apply_diff` is to update the actual /// effect our stored state, because the purpose of `apply_diff` is to update the actual
/// window's state to match our stored state. This controls whether to accept those changes. /// window's state to match our stored state. This controls whether to accept those changes.
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 11; const MARKER_RETAIN_STATE_ON_SIZE = 1 << 12;
const MARKER_IN_SIZE_MOVE = 1 << 12; const MARKER_IN_SIZE_MOVE = 1 << 13;
const MINIMIZED = 1 << 13; const MINIMIZED = 1 << 14;
const IGNORE_CURSOR_EVENT = 1 << 14; const IGNORE_CURSOR_EVENT = 1 << 15;
/// Fully decorated window (incl. caption, border and drop shadow). /// Fully decorated window (incl. caption, border and drop shadow).
const MARKER_DECORATIONS = 1 << 15; const MARKER_DECORATIONS = 1 << 16;
/// Drop shadow for undecorated windows. /// Drop shadow for undecorated windows.
const MARKER_UNDECORATED_SHADOW = 1 << 16; const MARKER_UNDECORATED_SHADOW = 1 << 17;
const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits;
} }
@ -301,13 +302,18 @@ impl WindowFlags {
} }
} }
if diff.contains(WindowFlags::ALWAYS_ON_TOP) { if diff.intersects(WindowFlags::ALWAYS_ON_TOP | WindowFlags::ALWAYS_ON_BOTTOM) {
unsafe { unsafe {
SetWindowPos( SetWindowPos(
window, window,
match new.contains(WindowFlags::ALWAYS_ON_TOP) { match (
true => HWND_TOPMOST, new.contains(WindowFlags::ALWAYS_ON_TOP),
false => HWND_NOTOPMOST, new.contains(WindowFlags::ALWAYS_ON_BOTTOM),
) {
(true, false) => HWND_TOPMOST,
(false, false) => HWND_NOTOPMOST,
(false, true) => HWND_BOTTOM,
(true, true) => unreachable!(),
}, },
0, 0,
0, 0,

View file

@ -132,11 +132,11 @@ pub(crate) struct WindowAttributes {
pub visible: bool, pub visible: bool,
pub transparent: bool, pub transparent: bool,
pub decorations: bool, pub decorations: bool,
pub always_on_top: bool,
pub window_icon: Option<Icon>, pub window_icon: Option<Icon>,
pub preferred_theme: Option<Theme>, pub preferred_theme: Option<Theme>,
pub resize_increments: Option<Size>, pub resize_increments: Option<Size>,
pub content_protected: bool, pub content_protected: bool,
pub window_level: WindowLevel,
} }
impl Default for WindowAttributes { impl Default for WindowAttributes {
@ -154,7 +154,7 @@ impl Default for WindowAttributes {
visible: true, visible: true,
transparent: false, transparent: false,
decorations: true, decorations: true,
always_on_top: false, window_level: Default::default(),
window_icon: None, window_icon: None,
preferred_theme: None, preferred_theme: None,
resize_increments: None, resize_increments: None,
@ -317,14 +317,16 @@ impl WindowBuilder {
self self
} }
/// Sets whether or not the window will always be on top of other windows. /// Sets the window level.
/// ///
/// The default is `false`. /// This is just a hint to the OS, and the system could ignore it.
/// ///
/// See [`Window::set_always_on_top`] for details. /// The default is [`WindowLevel::Normal`].
///
/// See [`WindowLevel`] for details.
#[inline] #[inline]
pub fn with_always_on_top(mut self, always_on_top: bool) -> Self { pub fn with_window_level(mut self, level: WindowLevel) -> Self {
self.window.always_on_top = always_on_top; self.window.window_level = level;
self self
} }
@ -840,14 +842,13 @@ impl Window {
self.window.is_decorated() self.window.is_decorated()
} }
/// Change whether or not the window will always be on top of other windows. /// Change the window level.
/// ///
/// ## Platform-specific /// This is just a hint to the OS, and the system could ignore it.
/// ///
/// - **iOS / Android / Web / Wayland:** Unsupported. /// See [`WindowLevel`] for details.
#[inline] pub fn set_window_level(&self, level: WindowLevel) {
pub fn set_always_on_top(&self, always_on_top: bool) { self.window.set_window_level(level)
self.window.set_always_on_top(always_on_top)
} }
/// Sets the window icon. /// Sets the window icon.
@ -1422,3 +1423,29 @@ impl Default for UserAttentionType {
UserAttentionType::Informational UserAttentionType::Informational
} }
} }
/// A window level groups windows with respect to their z-position.
///
/// The relative ordering between windows in different window levels is fixed.
/// The z-order of a window within the same window level may change dynamically on user interaction.
///
/// ## Platform-specific
///
/// - **iOS / Android / Web / Wayland:** Unsupported.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum WindowLevel {
/// The window will always be below normal windows.
///
/// This is useful for a widget-based app.
AlwaysOnBottom,
/// The default.
Normal,
/// The window will always be on top of normal windows.
AlwaysOnTop,
}
impl Default for WindowLevel {
fn default() -> Self {
Self::Normal
}
}