diff --git a/.changes/is-always-on-top.md b/.changes/is-always-on-top.md new file mode 100644 index 0000000000..0c69ca1315 --- /dev/null +++ b/.changes/is-always-on-top.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Add `Window::is_always_on_top` method to check if a window is always on top on macOS, Linux and Windows. diff --git a/CHANGELOG.md b/CHANGELOG.md index e35947c8cb..04ed1c6f7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[0.30.2] + +- [`016e122c`](https://github.com/tauri-apps/tao/commit/016e122c5f10eb61f8abe052a888950a460e0804) ([#978](https://github.com/tauri-apps/tao/pull/978) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) Fix changing the theme activates the window on Windows + ## \[0.30.1] - [`ad652e50`](https://github.com/tauri-apps/tao/commit/ad652e50bfca1195481cd347ccaa486818f9334d) ([#969](https://github.com/tauri-apps/tao/pull/969) by [@CampioneDev](https://github.com/tauri-apps/tao/../../CampioneDev)) On iOS, implement `application:openURL:options:` to handle custom URL schemes. diff --git a/Cargo.toml b/Cargo.toml index c2913aaa02..89fbb557d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tao" -version = "0.30.1" +version = "0.30.2" description = "Cross-platform window manager library." authors = [ "Tauri Programme within The Commons Conservancy", diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 896f064d5a..069272311a 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -569,6 +569,11 @@ impl Window { false } + pub fn is_always_on_top(&self) -> bool { + log::warn!("`Window::is_always_on_top` is ignored on Android"); + false + } + pub fn set_resizable(&self, _resizeable: bool) { warn!("`Window::set_resizable` is ignored on Android") } diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 62164ae4b8..d24f63d223 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -77,6 +77,11 @@ impl Inner { false } + pub fn is_always_on_top(&self) -> bool { + log::warn!("`Window::is_always_on_top` is ignored on iOS"); + false + } + pub fn request_redraw(&self) { unsafe { if self.gl_or_metal_backed { diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index b5ced655d9..af66a38a28 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -61,6 +61,7 @@ pub struct Window { position: Rc<(AtomicI32, AtomicI32)>, size: Rc<(AtomicI32, AtomicI32)>, maximized: Rc, + is_always_on_top: Rc, minimized: Rc, fullscreen: RefCell>, inner_size_constraints: RefCell, @@ -266,11 +267,14 @@ impl Window { let max_clone = maximized.clone(); let minimized = Rc::new(AtomicBool::new(false)); let minimized_clone = minimized.clone(); + let is_always_on_top = Rc::new(AtomicBool::new(attributes.always_on_top)); + let is_always_on_top_clone = is_always_on_top.clone(); window.connect_window_state_event(move |_, event| { let state = event.new_window_state(); max_clone.store(state.contains(WindowState::MAXIMIZED), Ordering::Release); minimized_clone.store(state.contains(WindowState::ICONIFIED), Ordering::Release); + is_always_on_top_clone.store(state.contains(WindowState::ABOVE), Ordering::Release); glib::Propagation::Proceed }); @@ -312,6 +316,7 @@ impl Window { size, maximized, minimized, + is_always_on_top, fullscreen: RefCell::new(attributes.fullscreen), inner_size_constraints: RefCell::new(attributes.inner_size_constraints), preferred_theme: RefCell::new(preferred_theme), @@ -362,11 +367,14 @@ impl Window { let max_clone = maximized.clone(); let minimized = Rc::new(AtomicBool::new(false)); let minimized_clone = minimized.clone(); + let is_always_on_top = Rc::new(AtomicBool::new(false)); + let is_always_on_top_clone = is_always_on_top.clone(); window.connect_window_state_event(move |_, event| { let state = event.new_window_state(); max_clone.store(state.contains(WindowState::MAXIMIZED), Ordering::Release); minimized_clone.store(state.contains(WindowState::ICONIFIED), Ordering::Release); + is_always_on_top_clone.store(state.contains(WindowState::ABOVE), Ordering::Release); glib::Propagation::Proceed }); @@ -391,6 +399,7 @@ impl Window { size, maximized, minimized, + is_always_on_top, fullscreen: RefCell::new(None), inner_size_constraints: RefCell::new(WindowSizeConstraints::default()), preferred_theme: RefCell::new(None), @@ -587,6 +596,10 @@ impl Window { } } + pub fn is_always_on_top(&self) -> bool { + self.is_always_on_top.load(Ordering::Acquire) + } + pub fn is_maximized(&self) -> bool { self.maximized.load(Ordering::Acquire) } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 49d420ea5c..0564a8f3f5 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -990,6 +990,11 @@ impl UnownedWindow { is_visible == YES } + #[inline] + pub fn is_always_on_top(&self) -> bool { + unsafe { self.ns_window.level() == ffi::kCGFloatingWindowLevelKey } + } + #[inline] pub fn is_maximized(&self) -> bool { self.is_zoomed() diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index b12f98b4b8..85a907c0c5 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -12,7 +12,7 @@ use windows::{ Foundation::{BOOL, HANDLE, HMODULE, HWND, WPARAM}, Graphics::Dwm::{DwmSetWindowAttribute, DWMWINDOWATTRIBUTE}, System::LibraryLoader::*, - UI::{Accessibility::*, WindowsAndMessaging::*}, + UI::{Accessibility::*, Input::KeyboardAndMouse::GetActiveWindow, WindowsAndMessaging::*}, }, }; @@ -187,9 +187,14 @@ fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) { &dark_mode as *const BOOL as *const c_void, std::mem::size_of::() as u32, ); + if GetActiveWindow() == hwnd { + DefWindowProcW(hwnd, WM_NCACTIVATE, None, None); + DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None); + } else { + DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None); + DefWindowProcW(hwnd, WM_NCACTIVATE, None, None); + } } - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; } } } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index a3a1978c68..ea4f2a0dc6 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -583,6 +583,14 @@ impl Window { window_state.window_flags.contains(WindowFlags::MAXIMIZED) } + #[inline] + pub fn is_always_on_top(&self) -> bool { + let window_state = self.window_state.lock(); + window_state + .window_flags + .contains(WindowFlags::ALWAYS_ON_TOP) + } + #[inline] pub fn is_minimized(&self) -> bool { unsafe { IsIconic(self.hwnd()) }.as_bool() diff --git a/src/window.rs b/src/window.rs index 3b9d57c607..abe11c25be 100644 --- a/src/window.rs +++ b/src/window.rs @@ -809,6 +809,16 @@ impl Window { self.window.is_focused() } + /// Indicates whether the window is always on top of other windows. + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + #[inline] + pub fn is_always_on_top(&self) -> bool { + self.window.is_always_on_top() + } + /// Sets whether the window is resizable or not. /// /// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that event can still be