Skip to content

Commit

Permalink
Always on bottom (#522)
Browse files Browse the repository at this point in the history
* feat: always below bottom

* refactor: always on bottom

* refactor: remove unnecessary warning
  • Loading branch information
henry40408 committed Aug 21, 2022
1 parent 759b7db commit a2a7b72
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changes/always-on-bottom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": "patch"
---

Implement "always on bottom" as contrary to "always on top".
12 changes: 12 additions & 0 deletions examples/window_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ fn main() {
eprintln!(" (Q) Quit event loop");
eprintln!(" (V) Toggle visibility");
eprintln!(" (X) Toggle maximized");
eprintln!(" (T) Toggle always on top");
eprintln!(" (B) Toggle always on bottom");

let mut always_on_bottom = false;
let mut always_on_top = false;
let mut visible = true;

event_loop.run(move |event, _, control_flow| {
Expand Down Expand Up @@ -120,6 +124,14 @@ fn main() {
"x" => {
window.set_maximized(!window.is_maximized());
}
"t" => {
always_on_top = !always_on_top;
window.set_always_on_top(always_on_top);
}
"b" => {
always_on_bottom = !always_on_bottom;
window.set_always_on_bottom(always_on_bottom);
}
_ => (),
},
Event::WindowEvent {
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,8 @@ impl Window {

pub fn set_decorations(&self, _decorations: bool) {}

pub fn set_always_on_bottom(&self, _always_on_bottom: bool) {}

pub fn set_always_on_top(&self, _always_on_top: bool) {}

pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ impl Inner {
warn!("`Window::set_decorations` is ignored on iOS")
}

pub fn set_always_on_bottom(&self, _always_on_bottom: bool) {
warn!("`Window::set_always_on_bottom` is ignored on iOS")
}

pub fn set_always_on_top(&self, _always_on_top: bool) {
warn!("`Window::set_always_on_top` is ignored on iOS")
}
Expand Down
3 changes: 3 additions & 0 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ impl<T: 'static> EventLoop<T> {
None => window.unfullscreen(),
},
WindowRequest::Decorations(decorations) => window.set_decorated(decorations),
WindowRequest::AlwaysOnBottom(always_on_bottom) => {
window.set_keep_below(always_on_bottom)
}
WindowRequest::AlwaysOnTop(always_on_top) => window.set_keep_above(always_on_top),
WindowRequest::WindowIcon(window_icon) => {
if let Some(icon) = window_icon {
Expand Down
19 changes: 18 additions & 1 deletion src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,14 @@ impl Window {
window.set_visible(attributes.visible);
window.set_decorated(attributes.decorations);

window.set_keep_above(attributes.always_on_top);
if attributes.always_on_bottom {
window.set_keep_below(attributes.always_on_bottom);
}

if attributes.always_on_top {
window.set_keep_above(attributes.always_on_top);
}

if let Some(icon) = attributes.window_icon {
window.set_icon(Some(&icon.inner.into()));
}
Expand Down Expand Up @@ -552,6 +559,15 @@ impl Window {
}
}

pub fn set_always_on_bottom(&self, always_on_bottom: bool) {
if let Err(e) = self.window_requests_tx.send((
self.window_id,
WindowRequest::AlwaysOnBottom(always_on_bottom),
)) {
log::warn!("Fail to send always on bottom request: {}", e);
}
}

pub fn set_always_on_top(&self, always_on_top: bool) {
if let Err(e) = self
.window_requests_tx
Expand Down Expand Up @@ -757,6 +773,7 @@ pub enum WindowRequest {
DragWindow,
Fullscreen(Option<Fullscreen>),
Decorations(bool),
AlwaysOnBottom(bool),
AlwaysOnTop(bool),
WindowIcon(Option<Icon>),
UserAttention(Option<UserAttentionType>),
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/macos/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;
#[derive(Debug, Clone, Copy)]
#[repr(isize)]
pub enum NSWindowLevel {
BelowNormalWindowLevel = (kCGBaseWindowLevelKey - 1) as _,
NSNormalWindowLevel = kCGBaseWindowLevelKey as _,
NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _,
NSTornOffMenuWindowLevel = kCGTornOffMenuWindowLevelKey as _,
Expand Down
17 changes: 17 additions & 0 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ fn create_window(
];
}

if attrs.always_on_bottom {
let _: () = msg_send![
*ns_window,
setLevel: ffi::NSWindowLevel::BelowNormalWindowLevel
];
}

if let Some(increments) = pl_attrs.resize_increments {
let (x, y) = (increments.width, increments.height);
if x >= 1.0 && y >= 1.0 {
Expand Down Expand Up @@ -1125,6 +1132,16 @@ impl UnownedWindow {
}
}

#[inline]
pub fn set_always_on_bottom(&self, always_on_bottom: bool) {
let level = if always_on_bottom {
ffi::NSWindowLevel::BelowNormalWindowLevel
} else {
ffi::NSWindowLevel::NSNormalWindowLevel
};
unsafe { util::set_level_async(*self.ns_window, level) };
}

#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
let level = if always_on_top {
Expand Down
7 changes: 7 additions & 0 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,7 @@ unsafe fn public_window_callback_inner<T: 'static>(

win32wm::WM_WINDOWPOSCHANGING => {
let mut window_state = subclass_input.window_state.lock();

if let Some(ref mut fullscreen) = window_state.fullscreen {
let window_pos = &mut *(lparam.0 as *mut WINDOWPOS);
let new_rect = RECT {
Expand Down Expand Up @@ -1151,6 +1152,12 @@ unsafe fn public_window_callback_inner<T: 'static>(
}
}

let window_flags = window_state.window_flags;
if window_flags.contains(WindowFlags::ALWAYS_ON_BOTTOM) {
let window_pos = &mut *(lparam.0 as *mut WINDOWPOS);
window_pos.hwndInsertAfter = HWND_BOTTOM;
}

result = ProcResult::Value(LRESULT(0));
}

Expand Down
13 changes: 13 additions & 0 deletions src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,18 @@ impl Window {
});
}

#[inline]
pub fn set_always_on_bottom(&self, always_on_bottom: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);

self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::ALWAYS_ON_BOTTOM, always_on_bottom)
});
});
}

#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
let window = self.window.clone();
Expand Down Expand Up @@ -843,6 +855,7 @@ unsafe fn init<T: 'static>(

let mut window_flags = WindowFlags::empty();
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
window_flags.set(WindowFlags::ALWAYS_ON_BOTTOM, attributes.always_on_bottom);
window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top);
window_flags.set(
WindowFlags::NO_BACK_BUFFER,
Expand Down
39 changes: 29 additions & 10 deletions src/platform_impl/windows/window_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,17 @@ bitflags! {
}
bitflags! {
pub struct WindowFlags: u32 {
const RESIZABLE = 1 << 0;
const DECORATIONS = 1 << 1;
const VISIBLE = 1 << 2;
const ON_TASKBAR = 1 << 3;
const ALWAYS_ON_TOP = 1 << 4;
const NO_BACK_BUFFER = 1 << 5;
const TRANSPARENT = 1 << 6;
const CHILD = 1 << 7;
const MAXIMIZED = 1 << 8;
const POPUP = 1 << 14;
const RESIZABLE = 1 << 0;
const DECORATIONS = 1 << 1;
const VISIBLE = 1 << 2;
const ON_TASKBAR = 1 << 3;
const ALWAYS_ON_TOP = 1 << 4;
const NO_BACK_BUFFER = 1 << 5;
const TRANSPARENT = 1 << 6;
const CHILD = 1 << 7;
const MAXIMIZED = 1 << 8;
const POPUP = 1 << 14;
const ALWAYS_ON_BOTTOM = 1 << 16;

/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
/// included here to make masking easier.
Expand Down Expand Up @@ -297,6 +298,24 @@ impl WindowFlags {
}
}

if diff.contains(WindowFlags::ALWAYS_ON_BOTTOM) {
unsafe {
SetWindowPos(
window,
match new.contains(WindowFlags::ALWAYS_ON_BOTTOM) {
true => HWND_BOTTOM,
false => HWND_NOTOPMOST,
},
0,
0,
0,
0,
SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,
);
InvalidateRgn(window, HRGN::default(), false);
}
}

if diff.contains(WindowFlags::MAXIMIZED) || new.contains(WindowFlags::MAXIMIZED) {
unsafe {
ShowWindow(
Expand Down
30 changes: 30 additions & 0 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ pub struct WindowAttributes {
/// The default is `false`.
pub always_on_top: bool,

/// Whether the window should always be on bottom of other windows.
///
/// The default is `false`.
pub always_on_bottom: bool,

/// The window icon.
///
/// The default is `None`.
Expand Down Expand Up @@ -219,6 +224,7 @@ impl Default for WindowAttributes {
transparent: false,
decorations: true,
always_on_top: false,
always_on_bottom: false,
window_icon: None,
window_menu: None,
preferred_theme: None,
Expand Down Expand Up @@ -361,13 +367,26 @@ impl WindowBuilder {
self
}

/// Sets whether or not the window will always be below other windows.
///
/// See [`Window::set_always_on_bottom`] for details.
///
/// [`Window::set_always_on_bottom`]: crate::window::Window::set_always_on_bottom
#[inline]
pub fn with_always_on_bottom(mut self, always_on_bottom: bool) -> Self {
self.window.always_on_top = false;
self.window.always_on_bottom = always_on_bottom;
self
}

/// Sets whether or not the window will always be on top of other windows.
///
/// See [`Window::set_always_on_top`] for details.
///
/// [`Window::set_always_on_top`]: crate::window::Window::set_always_on_top
#[inline]
pub fn with_always_on_top(mut self, always_on_top: bool) -> Self {
self.window.always_on_bottom = false;
self.window.always_on_top = always_on_top;
self
}
Expand Down Expand Up @@ -784,6 +803,17 @@ impl Window {
self.window.set_decorations(decorations)
}

/// Change whether or not the window will always be below other windows.
///
/// ## Platform-specific
///
/// - **Windows**: There is no guarantee that the window will be the bottom most but it will try to be.
/// - **iOS / Android:** Unsupported.
#[inline]
pub fn set_always_on_bottom(&self, always_on_bottom: bool) {
self.window.set_always_on_bottom(always_on_bottom)
}

/// Change whether or not the window will always be on top of other windows.
///
/// ## Platform-specific
Expand Down

0 comments on commit a2a7b72

Please sign in to comment.