From a4af50ec138bbe72146e0dfd63a670e087cde388 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Sat, 23 Aug 2025 20:55:54 +0800 Subject: [PATCH] win32: refresh title bar on `Window::set_theme` --- winit-win32/src/dark_mode.rs | 41 ++++++++++++++++++++++++------- winit-win32/src/event_loop.rs | 2 +- winit-win32/src/window.rs | 4 +-- winit/src/changelog/unreleased.md | 2 ++ 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/winit-win32/src/dark_mode.rs b/winit-win32/src/dark_mode.rs index 277c319c..7c3e5e08 100644 --- a/winit-win32/src/dark_mode.rs +++ b/winit-win32/src/dark_mode.rs @@ -3,13 +3,17 @@ use std::sync::LazyLock; /// which is inspired by the solution in https://github.com/ysc3839/win32-darkmode use std::{ffi::c_void, ptr}; -use windows_sys::core::PCSTR; -use windows_sys::Win32::Foundation::{BOOL, HWND, NTSTATUS, S_OK}; +use windows_sys::core::{PCSTR, PCWSTR}; +use windows_sys::w; +use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, NTSTATUS, S_OK, WPARAM}; use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW; use windows_sys::Win32::UI::Accessibility::{HCF_HIGHCONTRASTON, HIGHCONTRASTA}; use windows_sys::Win32::UI::Controls::SetWindowTheme; -use windows_sys::Win32::UI::WindowsAndMessaging::{SystemParametersInfoA, SPI_GETHIGHCONTRAST}; +use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow; +use windows_sys::Win32::UI::WindowsAndMessaging::{ + DefWindowProcW, SystemParametersInfoA, SPI_GETHIGHCONTRAST, WM_NCACTIVATE, +}; use winit_core::window::Theme; use super::util; @@ -51,13 +55,17 @@ static DARK_MODE_SUPPORTED: LazyLock = LazyLock::new(|| { } }); -static DARK_THEME_NAME: LazyLock> = - LazyLock::new(|| util::encode_wide("DarkMode_Explorer")); -static LIGHT_THEME_NAME: LazyLock> = LazyLock::new(|| util::encode_wide("")); +const DARK_THEME_NAME: PCWSTR = w!("DarkMode_Explorer"); +const LIGHT_THEME_NAME: PCWSTR = w!(""); /// Attempt to set a theme on a window, if necessary. /// Returns the theme that was picked -pub fn try_theme(hwnd: HWND, preferred_theme: Option) -> Theme { +/// +/// `refresh_title_bar` is only needed when the system doesn't do it by itself, +/// for cases like on window creation or system settings changes, +/// the system will refresh the title bar automatically, +/// if we always refresh the title bar, it will blink it on system settings changes +pub fn try_theme(hwnd: HWND, preferred_theme: Option, refresh_title_bar: bool) -> Theme { if *DARK_MODE_SUPPORTED { let is_dark_mode = match preferred_theme { Some(theme) => theme == Theme::Dark, @@ -66,13 +74,16 @@ pub fn try_theme(hwnd: HWND, preferred_theme: Option) -> Theme { let theme = if is_dark_mode { Theme::Dark } else { Theme::Light }; let theme_name = match theme { - Theme::Dark => DARK_THEME_NAME.as_ptr(), - Theme::Light => LIGHT_THEME_NAME.as_ptr(), + Theme::Dark => DARK_THEME_NAME, + Theme::Light => LIGHT_THEME_NAME, }; let status = unsafe { SetWindowTheme(hwnd, theme_name, ptr::null()) }; if status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode) { + if refresh_title_bar { + unsafe { refresh_titlebar_theme_color(hwnd) }; + } return theme; } } @@ -123,6 +134,18 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool { } } +unsafe fn refresh_titlebar_theme_color(hwnd: HWND) { + unsafe { + if GetActiveWindow() == hwnd { + DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM::default(), LPARAM::default()); + DefWindowProcW(hwnd, WM_NCACTIVATE, true as _, LPARAM::default()); + } else { + DefWindowProcW(hwnd, WM_NCACTIVATE, true as _, LPARAM::default()); + DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM::default(), LPARAM::default()); + } + } +} + pub fn should_use_dark_mode() -> bool { should_apps_use_dark_mode() && !is_high_contrast() } diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index 526a5966..e0807187 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -2467,7 +2467,7 @@ unsafe fn public_window_callback_inner( let preferred_theme = userdata.window_state_lock().preferred_theme; if preferred_theme.is_none() { - let new_theme = try_theme(window, preferred_theme); + let new_theme = try_theme(window, preferred_theme, false); let mut window_state = userdata.window_state_lock(); if window_state.current_theme != new_theme { diff --git a/winit-win32/src/window.rs b/winit-win32/src/window.rs index c1be6c97..61a9d2ed 100644 --- a/winit-win32/src/window.rs +++ b/winit-win32/src/window.rs @@ -1099,7 +1099,7 @@ impl CoreWindow for Window { } fn set_theme(&self, theme: Option) { - try_theme(self.window.hwnd(), theme); + self.window_state_lock().current_theme = try_theme(self.window.hwnd(), theme, true); } fn theme(&self) -> Option { @@ -1196,7 +1196,7 @@ impl InitData<'_> { // If the system theme is dark, we need to set the window theme now // before we update the window flags (and possibly show the // window for the first time). - let current_theme = try_theme(window, self.attributes.preferred_theme); + let current_theme = try_theme(window, self.attributes.preferred_theme, false); let window_state = { let window_state = WindowState::new( diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 7f5856fc..0d8f3240 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -257,3 +257,5 @@ changelog entry. - On macOS, don't panic on monitors with unknown bit-depths. - On macOS, fixed crash when closing the window on macOS 26+. - On Windows, account for mouse wheel lines per scroll setting for `WindowEvent::MouseWheel`. +- On Windows, `Window::theme` will return the correct theme after setting it through `Window::set_theme`. +- On Windows, `Window::set_theme` will change the title bar color immediately now.