From 89de496ab05344932d4070ccff1a4d423a4d138b Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 9 Jun 2024 15:46:18 +0800 Subject: [PATCH 01/44] Add a function to set theme dynamically --- src/platform_impl/windows/dark_mode.rs | 74 +++++++++++-------------- src/platform_impl/windows/event_loop.rs | 1 + src/platform_impl/windows/mod.rs | 8 +-- src/platform_impl/windows/window.rs | 19 +++++++ src/window.rs | 11 ++++ 5 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index bdc078f93..99ab3f03e 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -9,7 +9,8 @@ use once_cell::sync::Lazy; use windows::{ core::{s, w, PCSTR, PSTR}, Win32::{ - Foundation::{BOOL, HANDLE, HMODULE, HWND}, + Foundation::{BOOL, HANDLE, HMODULE, HWND, WPARAM}, + Graphics::Dwm::{DwmSetWindowAttribute, DWMWA_USE_IMMERSIVE_DARK_MODE}, System::LibraryLoader::*, UI::{Accessibility::*, WindowsAndMessaging::*}, }, @@ -118,7 +119,11 @@ fn refresh_immersive_color_policy_state() { /// Attempt to set a theme on a window, if necessary. /// Returns the theme that was picked -pub fn try_window_theme(hwnd: HWND, preferred_theme: Option) -> Theme { +pub fn try_window_theme( + hwnd: HWND, + preferred_theme: Option, + refresh_titilebar: bool, +) -> Theme { if *DARK_MODE_SUPPORTED { let is_dark_mode = match preferred_theme { Some(theme) => theme == Theme::Dark, @@ -130,7 +135,32 @@ pub fn try_window_theme(hwnd: HWND, preferred_theme: Option) -> Theme { false => Theme::Light, }; - refresh_titlebar_theme_color(hwnd, is_dark_mode); + if let Some(ver) = *WIN10_BUILD_VERSION { + if ver < 18362 { + let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); + unsafe { + let _ = SetPropW( + hwnd, + w!("UseImmersiveDarkModeColors"), + HANDLE(&mut is_dark_mode_bigbool as *mut _ as _), + ); + } + } else { + let dark_mode = BOOL::from(is_dark_mode); + unsafe { + let _ = DwmSetWindowAttribute( + hwnd, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &dark_mode as *const BOOL as *const c_void, + std::mem::size_of::() as u32, + ); + } + if refresh_titilebar { + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; + } + } + } theme } else { @@ -160,44 +190,6 @@ pub fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) { } } -type SetWindowCompositionAttribute = - unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL; -static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy> = - Lazy::new(|| get_function!("user32.dll", SetWindowCompositionAttribute)); - -type WINDOWCOMPOSITIONATTRIB = u32; -const WCA_USEDARKMODECOLORS: WINDOWCOMPOSITIONATTRIB = 26; -#[repr(C)] -struct WINDOWCOMPOSITIONATTRIBDATA { - Attrib: WINDOWCOMPOSITIONATTRIB, - pvData: *mut c_void, - cbData: usize, -} - -fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) { - // SetWindowCompositionAttribute needs a bigbool (i32), not bool. - let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); - - if let Some(ver) = *WIN10_BUILD_VERSION { - if ver < 18362 { - unsafe { - let _ = SetPropW( - hwnd, - w!("UseImmersiveDarkModeColors"), - HANDLE(&mut is_dark_mode_bigbool as *mut _ as _), - ); - } - } else if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE { - let mut data = WINDOWCOMPOSITIONATTRIBDATA { - Attrib: WCA_USEDARKMODECOLORS, - pvData: &mut is_dark_mode_bigbool as *mut _ as _, - cbData: std::mem::size_of_val(&is_dark_mode_bigbool) as _, - }; - let _ = unsafe { set_window_composition_attribute(hwnd, &mut data as *mut _) }; - } - } -} - fn should_use_dark_mode() -> bool { should_apps_use_dark_mode() && !is_high_contrast() } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 71f809038..22921277b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2064,6 +2064,7 @@ unsafe fn public_window_callback_inner( let new_theme = try_window_theme( window, preferred_theme.or(subclass_input.event_loop_preferred_theme), + false, ); let mut window_state = subclass_input.window_state.lock(); diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 7f8fd46ec..ae9834a85 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -65,10 +65,10 @@ unsafe impl Send for PlatformSpecificWindowBuilderAttributes {} unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {} // Cursor name in UTF-16. Used to set cursor in `WM_SETCURSOR`. -#[derive(Debug, Clone, Copy)] -pub struct Cursor(pub *const u16); -unsafe impl Send for Cursor {} -unsafe impl Sync for Cursor {} +// #[derive(Debug, Clone, Copy)] +// pub struct Cursor(pub *const u16); +// unsafe impl Send for Cursor {} +// unsafe impl Sync for Cursor {} #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId(isize); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 3b4b328ea..bc02d0572 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -903,6 +903,24 @@ impl Window { self.window_state.lock().current_theme } + pub fn set_theme(&self, theme: Option) { + let mut window_state = self.window_state.lock(); + if window_state.preferred_theme == theme { + return; + } + window_state.preferred_theme = theme; + if theme.is_none() { + unsafe { + let _ = PostMessageW(self.hwnd(), WM_WININICHANGE, WPARAM(0), LPARAM(0)); + }; + } else { + let new_theme = try_window_theme(self.hwnd(), theme, true); + if window_state.current_theme != new_theme { + window_state.current_theme = new_theme; + }; + } + } + #[inline] pub fn reset_dead_keys(&self) { // `ToUnicode` consumes the dead-key by default, so we are constructing a fake (but valid) @@ -1132,6 +1150,7 @@ unsafe fn init( let current_theme = try_window_theme( real_window.0, attributes.preferred_theme.or(event_loop.preferred_theme), + false, ); let window_state = { diff --git a/src/window.rs b/src/window.rs index 1a59e5c76..209b3de38 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1106,6 +1106,17 @@ impl Window { self.window.theme() } + /// Returns the current window theme. + /// + /// ## Platform-specific + /// + /// - **macOS/ Linux / iOS / Android:** Unsupported. + #[inline] + pub fn set_theme(&self, theme: Option) { + #[cfg(target_os = "windows")] + self.window.set_theme(theme) + } + /// Prevents the window contents from being captured by other apps. /// /// ## Platform-specific From 3a107a27bd18996de1afd9d5c4f97320d87d4be9 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 9 Jun 2024 16:09:52 +0800 Subject: [PATCH 02/44] Always refresh title bar and clippy --- src/platform_impl/windows/dark_mode.rs | 60 ++++++++++++------------- src/platform_impl/windows/event_loop.rs | 3 +- src/platform_impl/windows/window.rs | 11 +++-- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index 99ab3f03e..8cb6477ac 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -119,11 +119,7 @@ fn refresh_immersive_color_policy_state() { /// Attempt to set a theme on a window, if necessary. /// Returns the theme that was picked -pub fn try_window_theme( - hwnd: HWND, - preferred_theme: Option, - refresh_titilebar: bool, -) -> Theme { +pub fn try_window_theme(hwnd: HWND, preferred_theme: Option) -> Theme { if *DARK_MODE_SUPPORTED { let is_dark_mode = match preferred_theme { Some(theme) => theme == Theme::Dark, @@ -135,32 +131,7 @@ pub fn try_window_theme( false => Theme::Light, }; - if let Some(ver) = *WIN10_BUILD_VERSION { - if ver < 18362 { - let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); - unsafe { - let _ = SetPropW( - hwnd, - w!("UseImmersiveDarkModeColors"), - HANDLE(&mut is_dark_mode_bigbool as *mut _ as _), - ); - } - } else { - let dark_mode = BOOL::from(is_dark_mode); - unsafe { - let _ = DwmSetWindowAttribute( - hwnd, - DWMWA_USE_IMMERSIVE_DARK_MODE, - &dark_mode as *const BOOL as *const c_void, - std::mem::size_of::() as u32, - ); - } - if refresh_titilebar { - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; - } - } - } + refresh_titlebar_theme_color(hwnd, is_dark_mode); theme } else { @@ -190,6 +161,33 @@ pub fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) { } } +fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) { + if let Some(ver) = *WIN10_BUILD_VERSION { + if ver < 18362 { + let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); + unsafe { + let _ = SetPropW( + hwnd, + w!("UseImmersiveDarkModeColors"), + HANDLE(&mut is_dark_mode_bigbool as *mut _ as _), + ); + } + } else { + let dark_mode = BOOL::from(is_dark_mode); + unsafe { + let _ = DwmSetWindowAttribute( + hwnd, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &dark_mode as *const BOOL as *const c_void, + std::mem::size_of::() as u32, + ); + } + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; + } + } +} + fn should_use_dark_mode() -> bool { should_apps_use_dark_mode() && !is_high_contrast() } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 22921277b..ee8268a8c 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1185,7 +1185,7 @@ unsafe fn public_window_callback_inner( let windowpos = lparam.0 as *const WINDOWPOS; if (*windowpos).flags & SWP_NOMOVE != SWP_NOMOVE { - let physical_position = PhysicalPosition::new((*windowpos).x as i32, (*windowpos).y as i32); + let physical_position = PhysicalPosition::new((*windowpos).x, (*windowpos).y); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window.0)), event: Moved(physical_position), @@ -2064,7 +2064,6 @@ unsafe fn public_window_callback_inner( let new_theme = try_window_theme( window, preferred_theme.or(subclass_input.event_loop_preferred_theme), - false, ); let mut window_state = subclass_input.window_state.lock(); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index bc02d0572..769fbadf2 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -185,7 +185,7 @@ impl Window { #[inline] pub fn outer_position(&self) -> Result, NotSupportedError> { unsafe { util::get_window_rect(self.window.0) } - .map(|rect| Ok(PhysicalPosition::new(rect.left as i32, rect.top as i32))) + .map(|rect| Ok(PhysicalPosition::new(rect.left, rect.top))) .expect("Unexpected GetWindowRect failure") } @@ -195,7 +195,7 @@ impl Window { if !unsafe { ClientToScreen(self.window.0, &mut position) }.as_bool() { panic!("Unexpected ClientToScreen failure") } - Ok(PhysicalPosition::new(position.x as i32, position.y as i32)) + Ok(PhysicalPosition::new(position.x, position.y)) } #[inline] @@ -214,8 +214,8 @@ impl Window { let _ = SetWindowPos( self.window.0, HWND::default(), - x as i32, - y as i32, + x, + y, 0, 0, SWP_ASYNCWINDOWPOS | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE, @@ -914,7 +914,7 @@ impl Window { let _ = PostMessageW(self.hwnd(), WM_WININICHANGE, WPARAM(0), LPARAM(0)); }; } else { - let new_theme = try_window_theme(self.hwnd(), theme, true); + let new_theme = try_window_theme(self.hwnd(), theme); if window_state.current_theme != new_theme { window_state.current_theme = new_theme; }; @@ -1150,7 +1150,6 @@ unsafe fn init( let current_theme = try_window_theme( real_window.0, attributes.preferred_theme.or(event_loop.preferred_theme), - false, ); let window_state = { From f0b15fb4a7755fc0f5e6aad3f346c8071608ad3f Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 9 Jun 2024 16:53:03 +0800 Subject: [PATCH 03/44] Don't PostMessageW --- src/platform_impl/windows/event_loop.rs | 2 +- src/platform_impl/windows/window.rs | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index ee8268a8c..4a926f11b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2055,7 +2055,7 @@ unsafe fn public_window_callback_inner( result = ProcResult::Value(LRESULT(0)); } - win32wm::WM_WININICHANGE => { + win32wm::WM_SETTINGCHANGE => { use crate::event::WindowEvent::ThemeChanged; let preferred_theme = subclass_input.window_state.lock().preferred_theme; diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 769fbadf2..a61426244 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -909,16 +909,10 @@ impl Window { return; } window_state.preferred_theme = theme; - if theme.is_none() { - unsafe { - let _ = PostMessageW(self.hwnd(), WM_WININICHANGE, WPARAM(0), LPARAM(0)); - }; - } else { - let new_theme = try_window_theme(self.hwnd(), theme); - if window_state.current_theme != new_theme { - window_state.current_theme = new_theme; - }; - } + let new_theme = try_window_theme(self.hwnd(), theme); + if window_state.current_theme != new_theme { + window_state.current_theme = new_theme; + }; } #[inline] From fbacc5fa0c9a0f863a055bdb26eb5868b21f3d58 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 9 Jun 2024 18:04:42 +0800 Subject: [PATCH 04/44] Add change file --- .changes/set-theme-windows.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changes/set-theme-windows.md diff --git a/.changes/set-theme-windows.md b/.changes/set-theme-windows.md new file mode 100644 index 000000000..d978c00a0 --- /dev/null +++ b/.changes/set-theme-windows.md @@ -0,0 +1,5 @@ +--- +"tao": "patch" +--- + +Add a function `set_theme` to set theme dynamically after the window is created From 1691178307d03339dff81a1916e338359dfe5e3c Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 9 Jun 2024 18:45:26 +0800 Subject: [PATCH 05/44] windows only --- .changes/set-theme-windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/set-theme-windows.md b/.changes/set-theme-windows.md index d978c00a0..c02c019b1 100644 --- a/.changes/set-theme-windows.md +++ b/.changes/set-theme-windows.md @@ -2,4 +2,4 @@ "tao": "patch" --- -Add a function `set_theme` to set theme dynamically after the window is created +Add a function `set_theme` to set theme dynamically after the window is created, currently only implemented on Windows From 5d612d8334159b411dabd398fa5267e8c7e3fe23 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 9 Jun 2024 19:37:53 +0800 Subject: [PATCH 06/44] Update example --- examples/theme.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/examples/theme.rs b/examples/theme.rs index fafff8b52..4ea0ff242 100644 --- a/examples/theme.rs +++ b/examples/theme.rs @@ -5,9 +5,9 @@ #[allow(clippy::single_match)] fn main() { use tao::{ - event::{Event, WindowEvent}, + event::{ElementState, Event, MouseButton, WindowEvent}, event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + window::{Theme, WindowBuilder}, }; env_logger::init(); @@ -21,10 +21,24 @@ fn main() { println!("Initial theme: {:?}", window.theme()); + let mut theme = true; + event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; match event { + Event::WindowEvent { + event: + WindowEvent::MouseInput { + button: MouseButton::Left, + state: ElementState::Pressed, + .. + }, + .. + } => { + theme = !theme; + window.set_theme(if theme { None } else { Some(Theme::Dark) }) + } Event::WindowEvent { event: WindowEvent::CloseRequested, .. @@ -34,7 +48,7 @@ fn main() { window_id, .. } if window_id == window.id() => { - println!("Theme is changed: {:?}", theme) + println!("Theme is changed: {theme:?}") } _ => (), } From c19e00c23eae4c20d6ac86c38ff38f0425183880 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Sun, 9 Jun 2024 23:10:24 +0800 Subject: [PATCH 07/44] Doc comment --- src/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window.rs b/src/window.rs index 209b3de38..d3e355bae 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1106,7 +1106,7 @@ impl Window { self.window.theme() } - /// Returns the current window theme. + /// Sets the theme for this window. /// /// ## Platform-specific /// From 7ba5d70b734be6c7c9ea24809222496c12060733 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 10 Jun 2024 10:56:38 +0800 Subject: [PATCH 08/44] Refactor theme example --- examples/theme.rs | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/examples/theme.rs b/examples/theme.rs index 4ea0ff242..fe1827f09 100644 --- a/examples/theme.rs +++ b/examples/theme.rs @@ -28,28 +28,24 @@ fn main() { match event { Event::WindowEvent { - event: - WindowEvent::MouseInput { - button: MouseButton::Left, - state: ElementState::Pressed, - .. - }, - .. - } => { - theme = !theme; - window.set_theme(if theme { None } else { Some(Theme::Dark) }) - } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - Event::WindowEvent { - event: WindowEvent::ThemeChanged(theme), - window_id, - .. - } if window_id == window.id() => { - println!("Theme is changed: {theme:?}") - } + event, window_id, .. + } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::MouseInput { + button: MouseButton::Left, + state: ElementState::Pressed, + .. + } => { + theme = !theme; + window.set_theme(if theme { None } else { Some(Theme::Dark) }) + } + WindowEvent::ThemeChanged(theme) => { + if window_id == window.id() { + println!("Theme is changed: {theme:?}") + } + } + _ => (), + }, _ => (), } }); From e66ba7cb6c268287ebbd97513215c74ab02a84ba Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 10 Jun 2024 20:38:13 +0800 Subject: [PATCH 09/44] Send ThemeChanged event --- src/platform_impl/windows/event_loop.rs | 18 ++++++++++++++++++ src/platform_impl/windows/window.rs | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4a926f11b..8e892477d 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -611,6 +611,14 @@ lazy_static! { RegisterWindowMessageA(s!("Tao::DestroyMsg")) } }; + /// Message sent by a `Window` when a new theme is set. + /// WPARAM is 1 for dark mode and 0 for light mode. + /// and LPARAM is unused. + pub static ref SET_THEME_MSG_ID: u32 = { + unsafe { + RegisterWindowMessageA(s!("Tao::SetTheme")) + } + }; /// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the /// documentation in the `window_state` module for more information. pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { @@ -2184,6 +2192,16 @@ unsafe fn public_window_callback_inner( f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam.0 != 0) }); result = ProcResult::Value(LRESULT(0)); + } else if msg == *SET_THEME_MSG_ID { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window.0)), + event: WindowEvent::ThemeChanged(if wparam == WPARAM(1) { + Theme::Dark + } else { + Theme::Light + }), + }); + result = ProcResult::Value(LRESULT(0)); } else if msg == *S_U_TASKBAR_RESTART { let window_state = subclass_input.window_state.lock(); set_skip_taskbar(window, window_state.skip_taskbar); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index a61426244..b34216587 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -55,7 +55,10 @@ use crate::{ }, }; -use super::keyboard::{KeyEventBuilder, KEY_EVENT_BUILDERS}; +use super::{ + event_loop::SET_THEME_MSG_ID, + keyboard::{KeyEventBuilder, KEY_EVENT_BUILDERS}, +}; /// The Win32 implementation of the main `Window` object. pub struct Window { @@ -912,6 +915,17 @@ impl Window { let new_theme = try_window_theme(self.hwnd(), theme); if window_state.current_theme != new_theme { window_state.current_theme = new_theme; + let _ = unsafe { + PostMessageW( + self.hwnd(), + *SET_THEME_MSG_ID, + WPARAM(match new_theme { + Theme::Dark => 1, + Theme::Light => 0, + }), + LPARAM(0), + ) + }; }; } From f0f7e40648b9f30046b2e470c640dd1d90cca55b Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 10 Jun 2024 20:44:26 +0800 Subject: [PATCH 10/44] Move SET_THEME_MSG_ID down --- src/platform_impl/windows/event_loop.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 8e892477d..82efeb985 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -611,19 +611,19 @@ lazy_static! { RegisterWindowMessageA(s!("Tao::DestroyMsg")) } }; - /// Message sent by a `Window` when a new theme is set. - /// WPARAM is 1 for dark mode and 0 for light mode. - /// and LPARAM is unused. - pub static ref SET_THEME_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA(s!("Tao::SetTheme")) - } - }; /// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the /// documentation in the `window_state` module for more information. pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { RegisterWindowMessageA(s!("Tao::SetRetainMaximized")) }; + /// Message sent by a `Window` when a new theme is set. + /// WPARAM is 1 for dark mode and 0 for light mode. + /// and LPARAM is unused. + pub static ref SET_THEME_MSG_ID: u32 = { + unsafe { + RegisterWindowMessageA(s!("Tao::SetTheme")) + } + }; /// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows /// When the application receives this message, it should assume that any taskbar icons it added have been removed and add them again. pub static ref S_U_TASKBAR_RESTART: u32 = unsafe { From 30b37b24b1eee53ff3bdc657366bd0157fbe1923 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 10 Jun 2024 20:54:08 +0800 Subject: [PATCH 11/44] Fix indent --- src/platform_impl/windows/event_loop.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 82efeb985..d24c41bbf 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -619,11 +619,9 @@ lazy_static! { /// Message sent by a `Window` when a new theme is set. /// WPARAM is 1 for dark mode and 0 for light mode. /// and LPARAM is unused. - pub static ref SET_THEME_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA(s!("Tao::SetTheme")) - } - }; + pub static ref SET_THEME_MSG_ID: u32 = unsafe { + RegisterWindowMessageA(s!("Tao::SetTheme")) + }; /// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows /// When the application receives this message, it should assume that any taskbar icons it added have been removed and add them again. pub static ref S_U_TASKBAR_RESTART: u32 = unsafe { From 29e6376ec093892d18193c6e508317678279a9a3 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 10 Jun 2024 21:02:43 +0800 Subject: [PATCH 12/44] Clippy --- src/platform_impl/windows/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index d24c41bbf..4d3e47ff9 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1416,7 +1416,7 @@ unsafe fn public_window_callback_inner( win32wm::WM_RBUTTONDOWN => { use crate::event::{ElementState::Pressed, MouseButton::Right, WindowEvent::MouseInput}; - capture_mouse(window, &mut *subclass_input.window_state.lock()); + capture_mouse(window, &mut subclass_input.window_state.lock()); let modifiers = update_modifiers(window, subclass_input); From 6bab6a303bd59ba2ddc1cdf3b08b3e1ae38bc638 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 10 Jun 2024 23:13:39 +0800 Subject: [PATCH 13/44] Only redraw title bar on set_theme --- src/platform_impl/windows/dark_mode.rs | 16 +++++++++++----- src/platform_impl/windows/event_loop.rs | 1 + src/platform_impl/windows/window.rs | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index 8cb6477ac..f25003f70 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -119,7 +119,11 @@ fn refresh_immersive_color_policy_state() { /// Attempt to set a theme on a window, if necessary. /// Returns the theme that was picked -pub fn try_window_theme(hwnd: HWND, preferred_theme: Option) -> Theme { +pub fn try_window_theme( + hwnd: HWND, + preferred_theme: Option, + redraw_title_bar: bool, +) -> Theme { if *DARK_MODE_SUPPORTED { let is_dark_mode = match preferred_theme { Some(theme) => theme == Theme::Dark, @@ -131,7 +135,7 @@ pub fn try_window_theme(hwnd: HWND, preferred_theme: Option) -> Theme { false => Theme::Light, }; - refresh_titlebar_theme_color(hwnd, is_dark_mode); + refresh_titlebar_theme_color(hwnd, is_dark_mode, redraw_title_bar); theme } else { @@ -161,7 +165,7 @@ pub fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) { } } -fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) { +fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool, redraw_title_bar: bool) { if let Some(ver) = *WIN10_BUILD_VERSION { if ver < 18362 { let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); @@ -182,8 +186,10 @@ fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) { std::mem::size_of::() as u32, ); } - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; + if redraw_title_bar { + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; + } } } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4d3e47ff9..9317e2f13 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2070,6 +2070,7 @@ unsafe fn public_window_callback_inner( let new_theme = try_window_theme( window, preferred_theme.or(subclass_input.event_loop_preferred_theme), + false, ); let mut window_state = subclass_input.window_state.lock(); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index b34216587..ffe7ab1a3 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -912,7 +912,7 @@ impl Window { return; } window_state.preferred_theme = theme; - let new_theme = try_window_theme(self.hwnd(), theme); + let new_theme = try_window_theme(self.hwnd(), theme, true); if window_state.current_theme != new_theme { window_state.current_theme = new_theme; let _ = unsafe { @@ -1158,6 +1158,7 @@ unsafe fn init( let current_theme = try_window_theme( real_window.0, attributes.preferred_theme.or(event_loop.preferred_theme), + false, ); let window_state = { From 400117daf8ee47338509b5ed581659546dccec7e Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 14 Jun 2024 11:04:32 +0800 Subject: [PATCH 14/44] Implement for mac --- src/platform_impl/macos/window.rs | 13 +++++++++---- src/window.rs | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index c59f71808..efacc15f4 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -337,10 +337,11 @@ pub(super) fn get_ns_theme() -> Theme { } } -pub(super) fn set_ns_theme(theme: Theme) { +pub(super) fn set_ns_theme(theme: Option) { let name = match theme { - Theme::Dark => "NSAppearanceNameDarkAqua", - Theme::Light => "NSAppearanceNameAqua", + Some(Theme::Dark) => "NSAppearanceNameDarkAqua", + Some(Theme::Light) => "NSAppearanceNameAqua", + None => nil, }; unsafe { let app_class = class!(NSApplication); @@ -547,7 +548,7 @@ impl UnownedWindow { match cloned_preferred_theme { Some(theme) => { - set_ns_theme(theme); + set_ns_theme(Some(theme)); let mut state = window.shared_state.lock().unwrap(); state.current_theme = theme.clone(); } @@ -1417,6 +1418,10 @@ impl UnownedWindow { state.current_theme } + pub fn set_theme(&self, theme: Option) { + set_ns_theme(theme); + } + pub fn set_content_protection(&self, enabled: bool) { unsafe { let _: () = msg_send![*self.ns_window, setSharingType: !enabled as i32]; diff --git a/src/window.rs b/src/window.rs index d3e355bae..e02e8b422 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1110,10 +1110,10 @@ impl Window { /// /// ## Platform-specific /// - /// - **macOS/ Linux / iOS / Android:** Unsupported. + /// - **Linux / iOS / Android:** Unsupported. #[inline] pub fn set_theme(&self, theme: Option) { - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "macos", target_os = "windows"))] self.window.set_theme(theme) } From e6626d8e736b4276d9cf356dbc125c792257fa6c Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 14 Jun 2024 11:04:52 +0800 Subject: [PATCH 15/44] Change file --- .changes/{set-theme-windows.md => set-theme.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .changes/{set-theme-windows.md => set-theme.md} (57%) diff --git a/.changes/set-theme-windows.md b/.changes/set-theme.md similarity index 57% rename from .changes/set-theme-windows.md rename to .changes/set-theme.md index c02c019b1..c353d256f 100644 --- a/.changes/set-theme-windows.md +++ b/.changes/set-theme.md @@ -2,4 +2,4 @@ "tao": "patch" --- -Add a function `set_theme` to set theme dynamically after the window is created, currently only implemented on Windows +Add a function `set_theme` to set theme dynamically after the window is created, currently only implemented on Windows and macOS From 80a743aeed73ecfa54c05d4f8d35047226d54567 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 14 Jun 2024 12:19:55 +0800 Subject: [PATCH 16/44] Fix mac compile --- src/platform_impl/macos/window.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index efacc15f4..a22f90f87 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -338,17 +338,19 @@ pub(super) fn get_ns_theme() -> Theme { } pub(super) fn set_ns_theme(theme: Option) { - let name = match theme { - Some(Theme::Dark) => "NSAppearanceNameDarkAqua", - Some(Theme::Light) => "NSAppearanceNameAqua", - None => nil, - }; unsafe { let app_class = class!(NSApplication); let app: id = msg_send![app_class, sharedApplication]; let has_theme: BOOL = msg_send![app, respondsToSelector: sel!(effectiveAppearance)]; if has_theme == YES { - let name = NSString::alloc(nil).init_str(name); + let name = if let Some(theme) = theme { + NSString::alloc(nil).init_str(match theme { + Theme::Dark => "NSAppearanceNameDarkAqua", + Theme::Light => "NSAppearanceNameAqua", + }) + } else { + nil + }; let appearance: id = msg_send![class!(NSAppearance), appearanceNamed: name]; let _: () = msg_send![app, setAppearance: appearance]; } From d42d1e69d5db49432ba743c687d275dc1f23b98e Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 14 Jun 2024 14:08:51 +0800 Subject: [PATCH 17/44] #[allow(unused)] for linux --- src/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window.rs b/src/window.rs index e02e8b422..4a3a5b863 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1112,7 +1112,7 @@ impl Window { /// /// - **Linux / iOS / Android:** Unsupported. #[inline] - pub fn set_theme(&self, theme: Option) { + pub fn set_theme(&self, #[allow(unused)] theme: Option) { #[cfg(any(target_os = "macos", target_os = "windows"))] self.window.set_theme(theme) } From 061547cc26faaf05f6de835ba44bb26da154033a Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 25 Jul 2024 16:59:24 +0800 Subject: [PATCH 18/44] Try implement for linux --- .changes/set-theme.md | 2 +- src/platform_impl/linux/event_loop.rs | 9 +++++++++ src/platform_impl/linux/window.rs | 19 +++++++++++++++---- src/window.rs | 12 ++++++++++-- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/.changes/set-theme.md b/.changes/set-theme.md index c353d256f..d978c00a0 100644 --- a/.changes/set-theme.md +++ b/.changes/set-theme.md @@ -2,4 +2,4 @@ "tao": "patch" --- -Add a function `set_theme` to set theme dynamically after the window is created, currently only implemented on Windows and macOS +Add a function `set_theme` to set theme dynamically after the window is created diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index 33ed9f3d9..d679fb6dd 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -392,6 +392,7 @@ impl EventLoop { }; } WindowRequest::ProgressBarState(_) => unreachable!(), + WindowRequest::SetTheme(_) => unreachable!(), WindowRequest::WireUpEvents { transparent, fullscreen, @@ -857,6 +858,14 @@ impl EventLoop { WindowRequest::ProgressBarState(state) => { taskbar.update(state); } + WindowRequest::SetTheme(theme) => { + if let Some(settings) = Settings::default() { + match theme { + Some(Theme::Dark) => settings.set_gtk_application_prefer_dark_theme(true), + Some(Theme::Light) | None => settings.set_gtk_application_prefer_dark_theme(false), + } + } + } _ => unreachable!(), } } diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 7bc9a44ba..fc0afbb89 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -65,7 +65,7 @@ pub struct Window { inner_size_constraints: RefCell, /// Draw event Sender draw_tx: crossbeam_channel::Sender, - preferred_theme: Option, + preferred_theme: std::sync::Mutex>, } impl Window { @@ -306,7 +306,7 @@ impl Window { minimized, fullscreen: RefCell::new(attributes.fullscreen), inner_size_constraints: RefCell::new(attributes.inner_size_constraints), - preferred_theme, + preferred_theme: std::sync::Mutex::new(preferred_theme), }; win.set_skip_taskbar(pl_attribs.skip_taskbar); @@ -385,7 +385,7 @@ impl Window { minimized, fullscreen: RefCell::new(None), inner_size_constraints: RefCell::new(WindowSizeConstraints::default()), - preferred_theme: None, + preferred_theme: std::sync::Mutex::new(None), }; Ok(win) @@ -939,7 +939,7 @@ impl Window { } pub fn theme(&self) -> Theme { - if let Some(theme) = self.preferred_theme { + if let Some(theme) = self.preferred_theme.lock().unwrap() { return theme; } @@ -952,6 +952,16 @@ impl Window { Theme::Light } + + pub fn set_theme(&self, theme: Option) { + self.preferred_theme.lock().unwrap().replace(theme); + if let Err(e) = self + .window_requests_tx + .send((WindowId::dummy(), WindowRequest::SetTheme(theme))) + { + log::warn!("Fail to send set theme request: {e}"); + } + } } // We need GtkWindow to initialize WebView, so we have to keep it in the field. @@ -990,6 +1000,7 @@ pub enum WindowRequest { }, SetVisibleOnAllWorkspaces(bool), ProgressBarState(ProgressBarState), + SetTheme(Option), } impl Drop for Window { diff --git a/src/window.rs b/src/window.rs index 4a3a5b863..8855bac34 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1110,10 +1110,18 @@ impl Window { /// /// ## Platform-specific /// - /// - **Linux / iOS / Android:** Unsupported. + /// - **iOS / Android:** Unsupported. #[inline] pub fn set_theme(&self, #[allow(unused)] theme: Option) { - #[cfg(any(target_os = "macos", target_os = "windows"))] + #[cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "macos", + ))] self.window.set_theme(theme) } From 9fa9c41709f177c346f89a65a88851838e6f75c9 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 25 Jul 2024 17:03:27 +0800 Subject: [PATCH 19/44] Set theme state on mac --- src/platform_impl/macos/window.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index a22f90f87..49d420ea5 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -1422,6 +1422,8 @@ impl UnownedWindow { pub fn set_theme(&self, theme: Option) { set_ns_theme(theme); + let mut state = self.shared_state.lock().unwrap(); + state.current_theme = theme.unwrap_or_else(get_ns_theme); } pub fn set_content_protection(&self, enabled: bool) { From 154b3f20cae4896e91d22eb9b315ba815580c366 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 25 Jul 2024 17:05:01 +0800 Subject: [PATCH 20/44] Fix linux import --- src/platform_impl/linux/event_loop.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index d679fb6dd..76cf61af4 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -21,6 +21,7 @@ use gtk::{ cairo, gdk, gio, glib::{self}, prelude::*, + Settings, }; use crate::{ @@ -33,7 +34,9 @@ use crate::{ keyboard::ModifiersState, monitor::MonitorHandle as RootMonitorHandle, platform_impl::platform::{device, DEVICE_ID}, - window::{CursorIcon, Fullscreen, ProgressBarState, ResizeDirection, WindowId as RootWindowId}, + window::{ + CursorIcon, Fullscreen, ProgressBarState, ResizeDirection, Theme, WindowId as RootWindowId, + }, }; use super::{ From 2fe54bed1939e66f7e4b1240aa12f29bb0bf8c47 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 25 Jul 2024 17:11:06 +0800 Subject: [PATCH 21/44] Fix set and get preferred_theme on linux --- src/platform_impl/linux/window.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index fc0afbb89..bda33c762 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -939,7 +939,7 @@ impl Window { } pub fn theme(&self) -> Theme { - if let Some(theme) = self.preferred_theme.lock().unwrap() { + if let Some(theme) = *self.preferred_theme.lock().unwrap() { return theme; } @@ -954,7 +954,10 @@ impl Window { } pub fn set_theme(&self, theme: Option) { - self.preferred_theme.lock().unwrap().replace(theme); + { + let preferred_theme = self.preferred_theme.lock().unwrap(); + preferred_theme = theme; + } if let Err(e) = self .window_requests_tx .send((WindowId::dummy(), WindowRequest::SetTheme(theme))) From 11c244d17d61c6d145956aa9d2ccb09b27b37a98 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 25 Jul 2024 17:13:49 +0800 Subject: [PATCH 22/44] missing deref --- src/platform_impl/linux/window.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index bda33c762..2aef9aa4c 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -955,8 +955,8 @@ impl Window { pub fn set_theme(&self, theme: Option) { { - let preferred_theme = self.preferred_theme.lock().unwrap(); - preferred_theme = theme; + let mut preferred_theme = self.preferred_theme.lock().unwrap(); + *preferred_theme = theme; } if let Err(e) = self .window_requests_tx From 6ebb6a2aa95fcfafd4ec511499318a1fee3804e7 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 17 Aug 2024 13:15:53 +0800 Subject: [PATCH 23/44] Fix for windows 0.58 --- src/platform_impl/windows/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 89d7e9125..7ec200c83 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2201,7 +2201,7 @@ unsafe fn public_window_callback_inner( result = ProcResult::Value(LRESULT(0)); } else if msg == *SET_THEME_MSG_ID { subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window.0)), + window_id: RootWindowId(WindowId(window.0 as _)), event: WindowEvent::ThemeChanged(if wparam == WPARAM(1) { Theme::Dark } else { From c18000fe323390c73a903acee8f443df1f6a49ab Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:06:27 +0800 Subject: [PATCH 24/44] Update src/platform_impl/windows/event_loop.rs Co-authored-by: Amr Bashir --- src/platform_impl/windows/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b74ccc3c5..fa9b1439d 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -620,7 +620,7 @@ lazy_static! { /// WPARAM is 1 for dark mode and 0 for light mode. /// and LPARAM is unused. pub static ref SET_THEME_MSG_ID: u32 = unsafe { - RegisterWindowMessageA(s!("Tao::SetTheme")) + RegisterWindowMessageA(s!("Tao::EmitTheme")) }; /// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows /// When the application receives this message, it should assume that any taskbar icons it added have been removed and add them again. From 740ba9252dca88be846cf00bb8ad7f2418b060c4 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 10:10:44 +0800 Subject: [PATCH 25/44] Rename to EMIT_THEME_MSG_ID --- src/platform_impl/windows/event_loop.rs | 4 ++-- src/platform_impl/windows/window.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index fa9b1439d..b583df4ee 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -619,7 +619,7 @@ lazy_static! { /// Message sent by a `Window` when a new theme is set. /// WPARAM is 1 for dark mode and 0 for light mode. /// and LPARAM is unused. - pub static ref SET_THEME_MSG_ID: u32 = unsafe { + pub static ref EMIT_THEME_MSG_ID: u32 = unsafe { RegisterWindowMessageA(s!("Tao::EmitTheme")) }; /// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows @@ -2199,7 +2199,7 @@ unsafe fn public_window_callback_inner( f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam.0 != 0) }); result = ProcResult::Value(LRESULT(0)); - } else if msg == *SET_THEME_MSG_ID { + } else if msg == *EMIT_THEME_MSG_ID { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window.0 as _)), event: WindowEvent::ThemeChanged(if wparam == WPARAM(1) { diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index fee076d8e..82856f14d 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -56,7 +56,7 @@ use crate::{ }; use super::{ - event_loop::SET_THEME_MSG_ID, + event_loop::EMIT_THEME_MSG_ID, keyboard::{KeyEventBuilder, KEY_EVENT_BUILDERS}, }; @@ -939,7 +939,7 @@ impl Window { let _ = unsafe { PostMessageW( self.hwnd(), - *SET_THEME_MSG_ID, + *EMIT_THEME_MSG_ID, WPARAM(match new_theme { Theme::Dark => 1, Theme::Light => 0, From 9372ae6c052637b4fc6139978183d601a9f43be3 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 10:59:15 +0800 Subject: [PATCH 26/44] Add platform specific note on set_theme --- src/window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/window.rs b/src/window.rs index 8855bac34..3c34c1ae9 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1110,6 +1110,7 @@ impl Window { /// /// ## Platform-specific /// + /// - **Linux / macOS**: Theme is app-wide and not specific to this window. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_theme(&self, #[allow(unused)] theme: Option) { From 6e9252fe8e42d1f39759d7aa1964126a29b86e55 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 11:09:49 +0800 Subject: [PATCH 27/44] Add set_theme to event loot target --- src/event_loop.rs | 21 +++++++++++++++++++-- src/platform_impl/linux/event_loop.rs | 12 +++++++++++- src/platform_impl/macos/event_loop.rs | 7 ++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/event_loop.rs b/src/event_loop.rs index dbc2146cc..f1091583e 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -17,8 +17,12 @@ use instant::Instant; use std::{error, fmt, marker::PhantomData, ops::Deref}; use crate::{ - dpi::PhysicalPosition, error::ExternalError, event::Event, monitor::MonitorHandle, platform_impl, - window::ProgressBarState, + dpi::PhysicalPosition, + error::ExternalError, + event::Event, + monitor::MonitorHandle, + platform_impl, + window::{ProgressBarState, Theme}, }; /// Provides a way to retrieve events from the system and from the windows that were registered to @@ -296,6 +300,19 @@ impl EventLoopWindowTarget { #[cfg(any(target_os = "linux", target_os = "macos"))] self.p.set_progress_bar(_progress) } + + /// Sets the theme for this window. + /// + /// ## Platform-specific + /// + /// - **Linux / macOS**: Theme is app-wide and not specific to this window. + /// - **Windows:** Unsupported. Use the set_theme function available in Window (Windows can have different themes for different windows) + /// - **iOS / Android:** Unsupported. + #[inline] + pub fn set_theme(&self, #[allow(unused)] theme: Option) { + #[cfg(any(target_os = "linux", target_os = "macos"))] + self.p.set_theme(theme) + } } #[cfg(feature = "rwh_05")] diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index 76cf61af4..6d84587b5 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -156,7 +156,17 @@ impl EventLoopWindowTarget { .window_requests_tx .send((WindowId::dummy(), WindowRequest::ProgressBarState(progress))) { - log::warn!("Fail to send update progress bar request: {}", e); + log::warn!("Fail to send update progress bar request: {e}"); + } + } + + #[inline] + pub fn set_theme(&self, theme: Option) { + if let Err(e) = self + .window_requests_tx + .send((WindowId::dummy(), WindowRequest::SetTheme(theme))) + { + log::warn!("Fail to send update theme request: {e}"); } } } diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 790d743a1..41c464eae 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -36,7 +36,7 @@ use crate::{ observer::*, util::{self, IdRef}, }, - platform_impl::set_progress_indicator, + platform_impl::{set_ns_theme, set_progress_indicator}, window::ProgressBarState, }; @@ -120,6 +120,11 @@ impl EventLoopWindowTarget { pub fn set_progress_bar(&self, progress: ProgressBarState) { set_progress_indicator(progress); } + + #[inline] + pub fn set_theme(&self, theme: Option) { + set_ns_theme(theme) + } } pub struct EventLoop { From 1e33dee1be490e13061c65cdf84c0c4ba2e39795 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 11:14:23 +0800 Subject: [PATCH 28/44] Missing import --- src/platform_impl/macos/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 41c464eae..80b734d93 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -37,7 +37,7 @@ use crate::{ util::{self, IdRef}, }, platform_impl::{set_ns_theme, set_progress_indicator}, - window::ProgressBarState, + window::{ProgressBarState, Theme}, }; #[derive(Default)] From 70f40ab7a4b886e0bcbc665ad8d68b12cd809a86 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 11:18:10 +0800 Subject: [PATCH 29/44] Wrong import --- src/platform_impl/macos/event_loop.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 80b734d93..28b5f0ec1 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -36,8 +36,8 @@ use crate::{ observer::*, util::{self, IdRef}, }, - platform_impl::{set_ns_theme, set_progress_indicator}, - window::{ProgressBarState, Theme}, + platform_impl::set_progress_indicator, + window::{set_ns_theme, ProgressBarState, Theme}, }; #[derive(Default)] From 9c399bd0bf188caf415f52f18a61789e417db5ec Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 11:22:29 +0800 Subject: [PATCH 30/44] Wrong import --- src/platform_impl/macos/event_loop.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 28b5f0ec1..5fd89b86a 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -37,9 +37,11 @@ use crate::{ util::{self, IdRef}, }, platform_impl::set_progress_indicator, - window::{set_ns_theme, ProgressBarState, Theme}, + window::{ProgressBarState, Theme}, }; +use super::window::set_ns_theme; + #[derive(Default)] pub struct PanicInfo { inner: Cell>>, From c626141feeda2a0f7e703b6501a02a46c99d619f Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 11:32:17 +0800 Subject: [PATCH 31/44] Use 19 on older versions --- src/platform_impl/windows/dark_mode.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index 5761d6b50..6ebc58d7d 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -10,7 +10,7 @@ use windows::{ core::{s, w, PCSTR, PSTR}, Win32::{ Foundation::{BOOL, HANDLE, HMODULE, HWND, WPARAM}, - Graphics::Dwm::{DwmSetWindowAttribute, DWMWA_USE_IMMERSIVE_DARK_MODE}, + Graphics::Dwm::{DwmSetWindowAttribute, DWMWINDOWATTRIBUTE}, System::LibraryLoader::*, UI::{Accessibility::*, WindowsAndMessaging::*}, }, @@ -167,7 +167,7 @@ pub fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) { fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool, redraw_title_bar: bool) { if let Some(ver) = *WIN10_BUILD_VERSION { - if ver < 18362 { + if ver < 17763 { let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); unsafe { let _ = SetPropW( @@ -177,11 +177,17 @@ fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool, redraw_title_bar ); } } else { + // https://github.com/MicrosoftDocs/sdk-api/pull/966/files + let dwmwa_use_immersive_dark_mode = if ver > 18985 { + DWMWINDOWATTRIBUTE(20) + } else { + DWMWINDOWATTRIBUTE(19) + }; let dark_mode = BOOL::from(is_dark_mode); unsafe { let _ = DwmSetWindowAttribute( hwnd, - DWMWA_USE_IMMERSIVE_DARK_MODE, + dwmwa_use_immersive_dark_mode, &dark_mode as *const BOOL as *const c_void, std::mem::size_of::() as u32, ); From b44c3784ab6358e5e7d6529cbd82b401c886a853 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:57:11 +0800 Subject: [PATCH 32/44] Apply suggestions from code review Co-authored-by: Amr Bashir --- src/event_loop.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/event_loop.rs b/src/event_loop.rs index f1091583e..ee2ea5b13 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -301,11 +301,10 @@ impl EventLoopWindowTarget { self.p.set_progress_bar(_progress) } - /// Sets the theme for this window. + /// Sets the theme for the application. /// /// ## Platform-specific /// - /// - **Linux / macOS**: Theme is app-wide and not specific to this window. /// - **Windows:** Unsupported. Use the set_theme function available in Window (Windows can have different themes for different windows) /// - **iOS / Android:** Unsupported. #[inline] From 5f090efdb783765a0e72a96ea41bc08f08be012d Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 13:57:54 +0800 Subject: [PATCH 33/44] Support set theme on event loop for windows --- src/event_loop.rs | 4 +-- src/platform_impl/windows/event_loop.rs | 40 ++++++++++++++++++++++--- src/platform_impl/windows/window.rs | 6 ++-- src/window.rs | 9 ++---- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/event_loop.rs b/src/event_loop.rs index ee2ea5b13..ff492004e 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -305,11 +305,9 @@ impl EventLoopWindowTarget { /// /// ## Platform-specific /// - /// - **Windows:** Unsupported. Use the set_theme function available in Window (Windows can have different themes for different windows) /// - **iOS / Android:** Unsupported. #[inline] - pub fn set_theme(&self, #[allow(unused)] theme: Option) { - #[cfg(any(target_os = "linux", target_os = "macos"))] + pub fn set_theme(&self, theme: Option) { self.p.set_theme(theme) } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b583df4ee..546ce64b0 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -106,7 +106,7 @@ pub(crate) struct SubclassInput { pub _file_drop_handler: Option, pub subclass_removed: Cell, pub recurse_depth: Cell, - pub event_loop_preferred_theme: Option, + pub event_loop_preferred_theme: Arc>>, } impl SubclassInput { @@ -161,7 +161,7 @@ impl Default for PlatformSpecificEventLoopAttributes { pub struct EventLoopWindowTarget { thread_id: u32, thread_msg_target: HWND, - pub(crate) preferred_theme: Option, + pub(crate) preferred_theme: Arc>>, pub(crate) runner_shared: EventLoopRunnerShared, } @@ -202,7 +202,7 @@ impl EventLoop { thread_id, thread_msg_target, runner_shared, - preferred_theme: attributes.preferred_theme, + preferred_theme: Arc::new(Mutex::new(attributes.preferred_theme)), }, _marker: PhantomData, }, @@ -330,6 +330,12 @@ impl EventLoopWindowTarget { pub fn cursor_position(&self) -> Result, ExternalError> { util::cursor_position().map_err(Into::into) } + + #[inline] + pub fn set_theme(&self, theme: Option) { + *self.preferred_theme.lock() = theme; + unsafe { PostMessageW(HWND_BROADCAST, *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)).unwrap() }; + } } fn main_thread_id() -> u32 { @@ -616,6 +622,11 @@ lazy_static! { pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { RegisterWindowMessageA(s!("Tao::SetRetainMaximized")) }; + /// Message sent by event loop when event loop's prefered theme changed. + /// WPARAM and LPARAM are unused. + pub static ref CHANGE_THEME_MSG_ID: u32 = unsafe { + RegisterWindowMessageA(s!("Tao::ChangeTheme")) + }; /// Message sent by a `Window` when a new theme is set. /// WPARAM is 1 for dark mode and 0 for light mode. /// and LPARAM is unused. @@ -2077,7 +2088,7 @@ unsafe fn public_window_callback_inner( if preferred_theme.is_none() { let new_theme = try_window_theme( window, - preferred_theme.or(subclass_input.event_loop_preferred_theme), + preferred_theme.or(*subclass_input.event_loop_preferred_theme.lock()), false, ); let mut window_state = subclass_input.window_state.lock(); @@ -2199,6 +2210,27 @@ unsafe fn public_window_callback_inner( f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam.0 != 0) }); result = ProcResult::Value(LRESULT(0)); + } else if msg == *CHANGE_THEME_MSG_ID { + let preferred_theme = subclass_input.window_state.lock().preferred_theme; + + if preferred_theme.is_none() { + let new_theme = try_window_theme( + window, + preferred_theme.or(*subclass_input.event_loop_preferred_theme.lock()), + true, + ); + let mut window_state = subclass_input.window_state.lock(); + + if window_state.current_theme != new_theme { + window_state.current_theme = new_theme; + mem::drop(window_state); + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window.0 as _)), + event: WindowEvent::ThemeChanged(new_theme), + }); + } + } + result = ProcResult::Value(LRESULT(0)); } else if msg == *EMIT_THEME_MSG_ID { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window.0 as _)), diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 82856f14d..31418d192 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -136,7 +136,7 @@ impl Window { _file_drop_handler: file_drop_handler, subclass_removed: Cell::new(false), recurse_depth: Cell::new(0), - event_loop_preferred_theme: event_loop.preferred_theme, + event_loop_preferred_theme: event_loop.preferred_theme.clone(), }; event_loop::subclass_window(win.window.0, subclass_input); @@ -1167,7 +1167,9 @@ unsafe fn init( // window for the first time). let current_theme = try_window_theme( real_window.0, - attributes.preferred_theme.or(event_loop.preferred_theme), + attributes + .preferred_theme + .or(*event_loop.preferred_theme.lock()), false, ); diff --git a/src/window.rs b/src/window.rs index 3c34c1ae9..3b9d57c60 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1421,18 +1421,13 @@ pub enum Fullscreen { } #[non_exhaustive] -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Default, Clone, Copy, Debug, PartialEq)] pub enum Theme { + #[default] Light, Dark, } -impl Default for Theme { - fn default() -> Self { - Theme::Light - } -} - #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq)] pub enum UserAttentionType { From ce0a2043bbf46fb3e7ea4d3accf048ce2c8d167f Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 14:00:52 +0800 Subject: [PATCH 34/44] Forgot to exclude mobile --- src/event_loop.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/event_loop.rs b/src/event_loop.rs index ff492004e..6565dde06 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -308,6 +308,15 @@ impl EventLoopWindowTarget { /// - **iOS / Android:** Unsupported. #[inline] pub fn set_theme(&self, theme: Option) { + #[cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "macos", + ))] self.p.set_theme(theme) } } From bceaae990f0285f0f07f91b1a15d481ab628bc56 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 14:50:04 +0800 Subject: [PATCH 35/44] Ignore post message error instead of panic --- src/platform_impl/windows/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 546ce64b0..8ca652fbd 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -334,7 +334,7 @@ impl EventLoopWindowTarget { #[inline] pub fn set_theme(&self, theme: Option) { *self.preferred_theme.lock() = theme; - unsafe { PostMessageW(HWND_BROADCAST, *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)).unwrap() }; + let _ = unsafe { PostMessageW(HWND_BROADCAST, *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)) }; } } From 1925ecb8594ff45e3bcdcaa3b90778844f120677 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 20:35:28 +0800 Subject: [PATCH 36/44] Use parking_lot::Mutex on linux preferred_theme --- src/platform_impl/linux/window.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index c296c707a..320b28410 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -17,6 +17,7 @@ use gtk::{ glib::{self, translate::ToGlibPtr}, }; use gtk::{prelude::*, Settings}; +use parking_lot::Mutex; use crate::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, @@ -65,7 +66,7 @@ pub struct Window { inner_size_constraints: RefCell, /// Draw event Sender draw_tx: crossbeam_channel::Sender, - preferred_theme: std::sync::Mutex>, + preferred_theme: Mutex>, } impl Window { @@ -306,7 +307,7 @@ impl Window { minimized, fullscreen: RefCell::new(attributes.fullscreen), inner_size_constraints: RefCell::new(attributes.inner_size_constraints), - preferred_theme: std::sync::Mutex::new(preferred_theme), + preferred_theme: Mutex::new(preferred_theme), }; win.set_skip_taskbar(pl_attribs.skip_taskbar); @@ -385,7 +386,7 @@ impl Window { minimized, fullscreen: RefCell::new(None), inner_size_constraints: RefCell::new(WindowSizeConstraints::default()), - preferred_theme: std::sync::Mutex::new(None), + preferred_theme: Mutex::new(None), }; Ok(win) From a908a349dea456e66cbad811bad9b7b15a37e6eb Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 18 Sep 2024 20:45:27 +0800 Subject: [PATCH 37/44] Fix compile --- src/platform_impl/linux/window.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 320b28410..23d62ecfb 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -942,7 +942,7 @@ impl Window { } pub fn theme(&self) -> Theme { - if let Some(theme) = *self.preferred_theme.lock().unwrap() { + if let Some(theme) = *self.preferred_theme.lock() { return theme; } @@ -957,10 +957,7 @@ impl Window { } pub fn set_theme(&self, theme: Option) { - { - let mut preferred_theme = self.preferred_theme.lock().unwrap(); - *preferred_theme = theme; - } + *self.preferred_theme.lock() = theme; if let Err(e) = self .window_requests_tx .send((WindowId::dummy(), WindowRequest::SetTheme(theme))) From 40ada0e0ad240db5590cb84b3c229fc5c9c8ff7a Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 19 Sep 2024 09:38:55 +0800 Subject: [PATCH 38/44] Only send change theme event to owned windows --- src/platform_impl/windows/event_loop.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 8ca652fbd..5e312e24a 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -334,7 +334,9 @@ impl EventLoopWindowTarget { #[inline] pub fn set_theme(&self, theme: Option) { *self.preferred_theme.lock() = theme; - let _ = unsafe { PostMessageW(HWND_BROADCAST, *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)) }; + self.runner_shared.owned_windows(|window| { + let _ = unsafe { PostMessageW(window, *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)) }; + }); } } From affd0b5de86af3f5435c0a8d9661d51eb743084a Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 19 Sep 2024 16:51:02 +0300 Subject: [PATCH 39/44] change file --- .changes/set-theme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/set-theme.md b/.changes/set-theme.md index d978c00a0..99d4ee513 100644 --- a/.changes/set-theme.md +++ b/.changes/set-theme.md @@ -2,4 +2,4 @@ "tao": "patch" --- -Add a function `set_theme` to set theme dynamically after the window is created +Add a function `Window::set_theme` and `EventLoopWindowTarget::set_them`to set theme after window or event loop creation. From 1fee12a04954f72810764045b35b2df6ad3b9704 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 19 Sep 2024 17:18:53 +0300 Subject: [PATCH 40/44] use `SendMessageW` with `WM_SETTINGCHANGE` instead --- src/platform_impl/windows/event_loop.rs | 28 +------------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 5e312e24a..7af754dc6 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -335,7 +335,7 @@ impl EventLoopWindowTarget { pub fn set_theme(&self, theme: Option) { *self.preferred_theme.lock() = theme; self.runner_shared.owned_windows(|window| { - let _ = unsafe { PostMessageW(window, *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)) }; + let _ = unsafe { SendMessageW(window, WM_SETTINGCHANGE, WPARAM(0), LPARAM(0)) }; }); } } @@ -624,11 +624,6 @@ lazy_static! { pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { RegisterWindowMessageA(s!("Tao::SetRetainMaximized")) }; - /// Message sent by event loop when event loop's prefered theme changed. - /// WPARAM and LPARAM are unused. - pub static ref CHANGE_THEME_MSG_ID: u32 = unsafe { - RegisterWindowMessageA(s!("Tao::ChangeTheme")) - }; /// Message sent by a `Window` when a new theme is set. /// WPARAM is 1 for dark mode and 0 for light mode. /// and LPARAM is unused. @@ -2212,27 +2207,6 @@ unsafe fn public_window_callback_inner( f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam.0 != 0) }); result = ProcResult::Value(LRESULT(0)); - } else if msg == *CHANGE_THEME_MSG_ID { - let preferred_theme = subclass_input.window_state.lock().preferred_theme; - - if preferred_theme.is_none() { - let new_theme = try_window_theme( - window, - preferred_theme.or(*subclass_input.event_loop_preferred_theme.lock()), - true, - ); - let mut window_state = subclass_input.window_state.lock(); - - if window_state.current_theme != new_theme { - window_state.current_theme = new_theme; - mem::drop(window_state); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window.0 as _)), - event: WindowEvent::ThemeChanged(new_theme), - }); - } - } - result = ProcResult::Value(LRESULT(0)); } else if msg == *EMIT_THEME_MSG_ID { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window.0 as _)), From 715f37a0035d39052102d643110667b294ab9cf3 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 19 Sep 2024 17:31:50 +0300 Subject: [PATCH 41/44] Cleanup manual theme events and duplicated code --- examples/theme.rs | 25 ++++++++------ src/platform_impl/windows/event_loop.rs | 45 +++++++------------------ src/platform_impl/windows/window.rs | 31 +++++------------ 3 files changed, 35 insertions(+), 66 deletions(-) diff --git a/examples/theme.rs b/examples/theme.rs index fe1827f09..e7ebfee77 100644 --- a/examples/theme.rs +++ b/examples/theme.rs @@ -2,10 +2,11 @@ // Copyright 2021-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -#[allow(clippy::single_match)] +use tao::{event::KeyEvent, keyboard::KeyCode}; + fn main() { use tao::{ - event::{ElementState, Event, MouseButton, WindowEvent}, + event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{Theme, WindowBuilder}, }; @@ -20,8 +21,9 @@ fn main() { .unwrap(); println!("Initial theme: {:?}", window.theme()); - - let mut theme = true; + println!("Press D for Dark Mode"); + println!("Press L for Light Mode"); + println!("Press A for Auto Mode"); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; @@ -31,14 +33,15 @@ fn main() { event, window_id, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::MouseInput { - button: MouseButton::Left, - state: ElementState::Pressed, + WindowEvent::KeyboardInput { + event: KeyEvent { physical_key, .. }, .. - } => { - theme = !theme; - window.set_theme(if theme { None } else { Some(Theme::Dark) }) - } + } => match physical_key { + KeyCode::KeyD => window.set_theme(Some(Theme::Dark)), + KeyCode::KeyL => window.set_theme(Some(Theme::Light)), + KeyCode::KeyA => window.set_theme(None), + _ => {} + }, WindowEvent::ThemeChanged(theme) => { if window_id == window.id() { println!("Theme is changed: {theme:?}") diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 7af754dc6..28d93ab38 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -624,12 +624,6 @@ lazy_static! { pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { RegisterWindowMessageA(s!("Tao::SetRetainMaximized")) }; - /// Message sent by a `Window` when a new theme is set. - /// WPARAM is 1 for dark mode and 0 for light mode. - /// and LPARAM is unused. - pub static ref EMIT_THEME_MSG_ID: u32 = unsafe { - RegisterWindowMessageA(s!("Tao::EmitTheme")) - }; /// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows /// When the application receives this message, it should assume that any taskbar icons it added have been removed and add them again. pub static ref S_U_TASKBAR_RESTART: u32 = unsafe { @@ -2081,23 +2075,20 @@ unsafe fn public_window_callback_inner( use crate::event::WindowEvent::ThemeChanged; let preferred_theme = subclass_input.window_state.lock().preferred_theme; + let new_theme = try_window_theme( + window, + preferred_theme.or(*subclass_input.event_loop_preferred_theme.lock()), + false, + ); + let mut window_state = subclass_input.window_state.lock(); - if preferred_theme.is_none() { - let new_theme = try_window_theme( - window, - preferred_theme.or(*subclass_input.event_loop_preferred_theme.lock()), - false, - ); - let mut window_state = subclass_input.window_state.lock(); - - if window_state.current_theme != new_theme { - window_state.current_theme = new_theme; - mem::drop(window_state); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window.0 as _)), - event: ThemeChanged(new_theme), - }); - } + if window_state.current_theme != new_theme { + window_state.current_theme = new_theme; + mem::drop(window_state); + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window.0 as _)), + event: ThemeChanged(new_theme), + }); } } @@ -2207,16 +2198,6 @@ unsafe fn public_window_callback_inner( f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam.0 != 0) }); result = ProcResult::Value(LRESULT(0)); - } else if msg == *EMIT_THEME_MSG_ID { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window.0 as _)), - event: WindowEvent::ThemeChanged(if wparam == WPARAM(1) { - Theme::Dark - } else { - Theme::Light - }), - }); - result = ProcResult::Value(LRESULT(0)); } else if msg == *S_U_TASKBAR_RESTART { let window_state = subclass_input.window_state.lock(); let _ = set_skip_taskbar(window, window_state.skip_taskbar); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 31418d192..b1625122b 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -55,10 +55,7 @@ use crate::{ }, }; -use super::{ - event_loop::EMIT_THEME_MSG_ID, - keyboard::{KeyEventBuilder, KEY_EVENT_BUILDERS}, -}; +use super::keyboard::{KeyEventBuilder, KEY_EVENT_BUILDERS}; /// A simple non-owning wrapper around a window. #[derive(Clone, Copy)] @@ -928,26 +925,14 @@ impl Window { } pub fn set_theme(&self, theme: Option) { - let mut window_state = self.window_state.lock(); - if window_state.preferred_theme == theme { - return; + { + let mut window_state = self.window_state.lock(); + if window_state.preferred_theme == theme { + return; + } + window_state.preferred_theme = theme; } - window_state.preferred_theme = theme; - let new_theme = try_window_theme(self.hwnd(), theme, true); - if window_state.current_theme != new_theme { - window_state.current_theme = new_theme; - let _ = unsafe { - PostMessageW( - self.hwnd(), - *EMIT_THEME_MSG_ID, - WPARAM(match new_theme { - Theme::Dark => 1, - Theme::Light => 0, - }), - LPARAM(0), - ) - }; - }; + unsafe { SendMessageW(self.hwnd(), WM_SETTINGCHANGE, WPARAM(0), LPARAM(0)) }; } #[inline] From 1b4b5b49a5676495cc2ad6181a717d98dde774ea Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 19 Sep 2024 17:34:53 +0300 Subject: [PATCH 42/44] cleanup example --- examples/theme.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/theme.rs b/examples/theme.rs index e7ebfee77..b92ecd008 100644 --- a/examples/theme.rs +++ b/examples/theme.rs @@ -29,9 +29,7 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { - event, window_id, .. - } => match event { + Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { event: KeyEvent { physical_key, .. }, @@ -43,9 +41,7 @@ fn main() { _ => {} }, WindowEvent::ThemeChanged(theme) => { - if window_id == window.id() { - println!("Theme is changed: {theme:?}") - } + println!("Theme is changed: {theme:?}") } _ => (), }, From e595125d96f80746cbd37ff3c22eaaa1f05ddc73 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 19 Sep 2024 17:39:11 +0300 Subject: [PATCH 43/44] use RefCell on Linux --- src/platform_impl/linux/window.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 23d62ecfb..07a1ca9bc 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -17,7 +17,6 @@ use gtk::{ glib::{self, translate::ToGlibPtr}, }; use gtk::{prelude::*, Settings}; -use parking_lot::Mutex; use crate::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, @@ -66,7 +65,7 @@ pub struct Window { inner_size_constraints: RefCell, /// Draw event Sender draw_tx: crossbeam_channel::Sender, - preferred_theme: Mutex>, + preferred_theme: RefCell>, } impl Window { @@ -307,7 +306,7 @@ impl Window { minimized, fullscreen: RefCell::new(attributes.fullscreen), inner_size_constraints: RefCell::new(attributes.inner_size_constraints), - preferred_theme: Mutex::new(preferred_theme), + preferred_theme: RefCell::new(preferred_theme), }; win.set_skip_taskbar(pl_attribs.skip_taskbar); @@ -386,7 +385,7 @@ impl Window { minimized, fullscreen: RefCell::new(None), inner_size_constraints: RefCell::new(WindowSizeConstraints::default()), - preferred_theme: Mutex::new(None), + preferred_theme: RefCell::new(None), }; Ok(win) @@ -942,7 +941,7 @@ impl Window { } pub fn theme(&self) -> Theme { - if let Some(theme) = *self.preferred_theme.lock() { + if let Some(theme) = *self.preferred_theme.borrow() { return theme; } @@ -957,7 +956,7 @@ impl Window { } pub fn set_theme(&self, theme: Option) { - *self.preferred_theme.lock() = theme; + *self.preferred_theme.borrow_mut() = theme; if let Err(e) = self .window_requests_tx .send((WindowId::dummy(), WindowRequest::SetTheme(theme))) From 0b1daad1c1d6b88f983fde6d07ea9d21241c4ff2 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 19 Sep 2024 17:43:01 +0300 Subject: [PATCH 44/44] always redraw titlebar --- src/platform_impl/windows/dark_mode.rs | 16 +++++----------- src/platform_impl/windows/event_loop.rs | 1 - src/platform_impl/windows/window.rs | 1 - 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index 6ebc58d7d..b12f98b4b 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -119,11 +119,7 @@ fn refresh_immersive_color_policy_state() { /// Attempt to set a theme on a window, if necessary. /// Returns the theme that was picked -pub fn try_window_theme( - hwnd: HWND, - preferred_theme: Option, - redraw_title_bar: bool, -) -> Theme { +pub fn try_window_theme(hwnd: HWND, preferred_theme: Option) -> Theme { if *DARK_MODE_SUPPORTED { let is_dark_mode = match preferred_theme { Some(theme) => theme == Theme::Dark, @@ -135,7 +131,7 @@ pub fn try_window_theme( false => Theme::Light, }; - refresh_titlebar_theme_color(hwnd, is_dark_mode, redraw_title_bar); + refresh_titlebar_theme_color(hwnd, is_dark_mode); theme } else { @@ -165,7 +161,7 @@ pub fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) { } } -fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool, redraw_title_bar: bool) { +fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) { if let Some(ver) = *WIN10_BUILD_VERSION { if ver < 17763 { let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); @@ -192,10 +188,8 @@ fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool, redraw_title_bar std::mem::size_of::() as u32, ); } - if redraw_title_bar { - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; - unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; - } + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) }; + unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) }; } } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 28d93ab38..7007ac89f 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2078,7 +2078,6 @@ unsafe fn public_window_callback_inner( let new_theme = try_window_theme( window, preferred_theme.or(*subclass_input.event_loop_preferred_theme.lock()), - false, ); let mut window_state = subclass_input.window_state.lock(); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index b1625122b..a3a1978c6 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -1155,7 +1155,6 @@ unsafe fn init( attributes .preferred_theme .or(*event_loop.preferred_theme.lock()), - false, ); let window_state = {