From 2fffdc9db73edbd529f4c3cf889b33029cefd955 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:14:12 +0800 Subject: [PATCH] fix(windows): blinking title bar on changing system settings (#983) * Only redraw on manual set theme * Add change file * Ignore update theme on settings change preferred theme is set --- .changes/windows-theme-redraw-titlebar.md | 5 +++ src/platform_impl/windows/dark_mode.rs | 24 ++++++---- src/platform_impl/windows/event_loop.rs | 53 +++++++++++++++-------- src/platform_impl/windows/window.rs | 8 +++- 4 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 .changes/windows-theme-redraw-titlebar.md diff --git a/.changes/windows-theme-redraw-titlebar.md b/.changes/windows-theme-redraw-titlebar.md new file mode 100644 index 000000000..f4ab37edb --- /dev/null +++ b/.changes/windows-theme-redraw-titlebar.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Fix blinking title bar when changing system settings on Windows diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index 85a907c0c..620e01c29 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 < 17763 { let mut is_dark_mode_bigbool: i32 = is_dark_mode.into(); @@ -187,12 +191,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); + if redraw_title_bar { + 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); + } } } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 7007ac89f..5b9d4db59 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 { SendMessageW(window, WM_SETTINGCHANGE, WPARAM(0), LPARAM(0)) }; + let _ = unsafe { SendMessageW(window, *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)) }; }); } } @@ -624,6 +624,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")) + }; /// 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 { @@ -2072,23 +2077,7 @@ unsafe fn public_window_callback_inner( } win32wm::WM_SETTINGCHANGE => { - 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()), - ); - 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), - }); - } + update_theme(subclass_input, window, true); } win32wm::WM_NCCALCSIZE => { @@ -2197,6 +2186,9 @@ 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 { + update_theme(subclass_input, window, false); + 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); @@ -2216,6 +2208,31 @@ unsafe fn public_window_callback_inner( } } +fn update_theme( + subclass_input: &SubclassInput, + window: HWND, + from_settings_change_event: bool, +) { + let mut window_state = subclass_input.window_state.lock(); + let preferred_theme = window_state + .preferred_theme + .or(*subclass_input.event_loop_preferred_theme.lock()); + if from_settings_change_event && preferred_theme.is_some() { + return; + } + let new_theme = try_window_theme(window, preferred_theme, !from_settings_change_event); + if window_state.current_theme != new_theme { + window_state.current_theme = new_theme; + mem::drop(window_state); + unsafe { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window.0 as _)), + event: WindowEvent::ThemeChanged(new_theme), + }) + }; + } +} + fn is_show_window_contents_while_dragging_enabled() -> bool { let mut is_enabled: BOOL = BOOL(0); let result = unsafe { diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index ea4f2a0dc..31ec275ea 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::CHANGE_THEME_MSG_ID, + keyboard::{KeyEventBuilder, KEY_EVENT_BUILDERS}, +}; /// A simple non-owning wrapper around a window. #[derive(Clone, Copy)] @@ -940,7 +943,7 @@ impl Window { } window_state.preferred_theme = theme; } - unsafe { SendMessageW(self.hwnd(), WM_SETTINGCHANGE, WPARAM(0), LPARAM(0)) }; + unsafe { SendMessageW(self.hwnd(), *CHANGE_THEME_MSG_ID, WPARAM(0), LPARAM(0)) }; } #[inline] @@ -1163,6 +1166,7 @@ unsafe fn init( attributes .preferred_theme .or(*event_loop.preferred_theme.lock()), + false, ); let window_state = {