Skip to content

Commit

Permalink
fix(windows): fix setting themes and maintain preferred theme when th…
Browse files Browse the repository at this point in the history
…e system changes its theme (#844)

closes #840
  • Loading branch information
amrbashir authored Nov 14, 2023
1 parent 5c22d76 commit fce9d26
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 49 deletions.
5 changes: 5 additions & 0 deletions .changes/windows-themes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": "patch"
---

On Windows, fix `WindowBuilder::with_theme` has no effect when forcing light theme on a dark mode system.
56 changes: 16 additions & 40 deletions src/platform_impl/windows/dark_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use once_cell::sync::Lazy;
/// This is a simple implementation of support for Windows Dark Mode,
/// which is inspired by the solution in https://github.com/ysc3839/win32-darkmode
use windows::{
core::{s, w, PCSTR, PSTR},
core::{s, w, PCSTR, PCWSTR, PSTR},
Win32::{
Foundation::{BOOL, HANDLE, HMODULE, HWND},
System::{LibraryLoader::*, SystemInformation::OSVERSIONINFOW},
UI::{Accessibility::*, WindowsAndMessaging::*},
UI::{Accessibility::*, Controls::SetWindowTheme, WindowsAndMessaging::*},
},
};

Expand Down Expand Up @@ -59,7 +59,7 @@ static DARK_MODE_SUPPORTED: Lazy<bool> = Lazy::new(|| {
});

/// Attempts to set dark mode for the app
pub fn try_app_theme(preferred_theme: Option<Theme>) -> Theme {
pub fn try_app_theme(preferred_theme: Option<Theme>) {
if *DARK_MODE_SUPPORTED {
let is_dark_mode = match preferred_theme {
Some(theme) => theme == Theme::Dark,
Expand All @@ -68,12 +68,6 @@ pub fn try_app_theme(preferred_theme: Option<Theme>) -> Theme {

allow_dark_mode_for_app(is_dark_mode);
refresh_immersive_color_policy_state();
match is_dark_mode {
true => Theme::Dark,
false => Theme::Light,
}
} else {
Theme::Light
}
}

Expand Down Expand Up @@ -160,14 +154,19 @@ pub fn try_window_theme(hwnd: HWND, preferred_theme: Option<Theme>) -> Theme {
None => should_use_dark_mode(),
};

let theme = if is_dark_mode {
Theme::Dark
} else {
Theme::Light
let theme = match is_dark_mode {
true => Theme::Dark,
false => Theme::Light,
};

let theme_name = match theme {
Theme::Dark => w!("DarkMode_Explorer"),
Theme::Light => w!(""),
};
let _ = unsafe { SetWindowTheme(hwnd, theme_name, PCWSTR::null()) };

allow_dark_mode_for_window(hwnd, is_dark_mode);
refresh_titlebar_theme_color(hwnd);
refresh_titlebar_theme_color(hwnd, is_dark_mode);

theme
} else {
Expand Down Expand Up @@ -197,29 +196,6 @@ fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) {
}
}

fn is_dark_mode_allowed_for_window(hwnd: HWND) -> bool {
const UXTHEME_ISDARKMODEALLOWEDFORWINDOW_ORDINAL: u16 = 137;
type IsDarkModeAllowedForWindow = unsafe extern "system" fn(HWND) -> bool;
static IS_DARK_MODE_ALLOWED_FOR_WINDOW: Lazy<Option<IsDarkModeAllowedForWindow>> =
Lazy::new(|| unsafe {
if HUXTHEME.is_invalid() {
return None;
}

GetProcAddress(
*HUXTHEME,
PCSTR::from_raw(UXTHEME_ISDARKMODEALLOWEDFORWINDOW_ORDINAL as usize as *mut _),
)
.map(|handle| std::mem::transmute(handle))
});

if let Some(_is_dark_mode_allowed_for_window) = *IS_DARK_MODE_ALLOWED_FOR_WINDOW {
unsafe { _is_dark_mode_allowed_for_window(hwnd) }
} else {
false
}
}

type SetWindowCompositionAttribute =
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy<Option<SetWindowCompositionAttribute>> =
Expand All @@ -234,9 +210,9 @@ struct WINDOWCOMPOSITIONATTRIBDATA {
cbData: usize,
}

fn refresh_titlebar_theme_color(hwnd: HWND) {
let dark = should_use_dark_mode() && is_dark_mode_allowed_for_window(hwnd);
let mut is_dark_mode_bigbool: BOOL = dark.into();
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 {
Expand Down
12 changes: 8 additions & 4 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ pub(crate) struct SubclassInput<T: 'static> {
pub _file_drop_handler: Option<IDropTarget>,
pub subclass_removed: Cell<bool>,
pub recurse_depth: Cell<u32>,
pub event_loop_preferred_theme: Option<Theme>,
}

impl<T> SubclassInput<T> {
Expand Down Expand Up @@ -161,7 +162,7 @@ impl Default for PlatformSpecificEventLoopAttributes {
pub struct EventLoopWindowTarget<T: 'static> {
thread_id: u32,
thread_msg_target: HWND,
pub(crate) theme: Theme,
pub(crate) preferred_theme: Option<Theme>,
pub(crate) runner_shared: EventLoopRunnerShared<T>,
}

Expand All @@ -184,7 +185,7 @@ impl<T: 'static> EventLoop<T> {

let thread_msg_target = create_event_target_window();

let theme = try_app_theme(attributes.preferred_theme);
try_app_theme(attributes.preferred_theme);

let send_thread_msg_target = thread_msg_target;
thread::spawn(move || wait_thread(thread_id, send_thread_msg_target));
Expand All @@ -202,7 +203,7 @@ impl<T: 'static> EventLoop<T> {
thread_id,
thread_msg_target,
runner_shared,
theme,
preferred_theme: attributes.preferred_theme,
},
_marker: PhantomData,
},
Expand Down Expand Up @@ -2056,7 +2057,10 @@ unsafe fn public_window_callback_inner<T: 'static>(
let preferred_theme = subclass_input.window_state.lock().preferred_theme;

if preferred_theme.is_none() {
let new_theme = try_window_theme(window, preferred_theme);
let new_theme = try_window_theme(
window,
preferred_theme.or(subclass_input.event_loop_preferred_theme),
);
let mut window_state = subclass_input.window_state.lock();

if window_state.current_theme != new_theme {
Expand Down
4 changes: 1 addition & 3 deletions src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub(crate) use self::{

pub use self::icon::WinIcon as PlatformIcon;

use crate::{event::DeviceId as RootDeviceId, icon::Icon, keyboard::Key, window::Theme};
use crate::{event::DeviceId as RootDeviceId, icon::Icon, keyboard::Key};
mod keycode;

#[non_exhaustive]
Expand All @@ -41,7 +41,6 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub window_classname: String,
pub no_redirection_bitmap: bool,
pub drag_and_drop: bool,
pub preferred_theme: Option<Theme>,
pub decoration_shadow: bool,
pub rtl: bool,
}
Expand All @@ -54,7 +53,6 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
taskbar_icon: None,
no_redirection_bitmap: false,
drag_and_drop: true,
preferred_theme: None,
skip_taskbar: false,
window_classname: "Window Class".to_string(),
decoration_shadow: true,
Expand Down
5 changes: 3 additions & 2 deletions src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,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::subclass_window(win.window.0, subclass_input);
Expand Down Expand Up @@ -1112,7 +1113,7 @@ unsafe fn init<T: 'static>(
// window for the first time).
let current_theme = try_window_theme(
real_window.0,
attributes.preferred_theme.or(Some(event_loop.theme)),
attributes.preferred_theme.or(event_loop.preferred_theme),
);

let window_state = {
Expand All @@ -1121,7 +1122,7 @@ unsafe fn init<T: 'static>(
None,
scale_factor,
current_theme,
pl_attribs.preferred_theme,
attributes.preferred_theme,
);
let window_state = Arc::new(Mutex::new(window_state));
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
Expand Down

0 comments on commit fce9d26

Please sign in to comment.