Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to force a theme on Windows #1666

Merged
merged 34 commits into from Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
486798a
You can now force either dark or light theme no matter the system pre…
VZout Aug 20, 2020
c80974e
Clean up and fixed a freeze when switching at runtime
VZout Aug 20, 2020
1d045f3
Update FEATURES.md
VZout Aug 20, 2020
fb78110
Update windows.rs
VZout Aug 20, 2020
b09b464
Update CHANGELOG.md
VZout Aug 20, 2020
45592dd
cargo fmt
VZout Aug 20, 2020
d92a4bd
Merge branch 'force_theme_v1' of github.com:VZout/winit into force_th…
VZout Aug 20, 2020
d210aa8
Update CHANGELOG.md
VZout Aug 20, 2020
3e9895b
Merge branch 'master' into force_theme_v1
VZout Oct 13, 2020
4b14626
Incorporated feedback
VZout Oct 13, 2020
34ee46e
fixed a comment
VZout Oct 13, 2020
885e66b
Merge branch 'force_theme_v1' of github.com:VZout/winit into force_th…
VZout Oct 13, 2020
1b6436f
Changelog update
VZout Oct 13, 2020
7ca4bb7
fmt
VZout Oct 13, 2020
2fdfd80
Merge branch 'master' into force_theme_v1
VZout Oct 14, 2020
9d347bb
Incorporated feedback
Oct 15, 2020
6762d84
Merge branch 'master' into force_theme_v1
VZout Oct 15, 2020
5a79a2f
Inlined the changed param
Oct 15, 2020
e8ff688
Merge branch 'force_theme_v1' of https://github.com/VZout/winit into …
Oct 15, 2020
43d3959
Update src/platform/windows.rs
VZout Nov 16, 2020
32ca08b
feedback
VZout Nov 16, 2020
452ebec
merged
VZout Nov 16, 2020
653efc2
kchibisov's suggestion
VZout Nov 16, 2020
3984155
Merge branch 'master' into force_theme_v1
VZout Nov 16, 2020
aa1248c
Update CHANGELOG.md
VZout Nov 23, 2020
9c797ad
reordered changelog
VZout Nov 23, 2020
d27490d
Update dark_mode.rs
VZout Nov 23, 2020
33ea8f4
renamed try_dark_mode to try_theme and fixed a unused import
VZout Nov 23, 2020
89c8a11
Merge branch 'master' into force_theme_v1
VZout Nov 27, 2020
2d9e151
fmt
VZout Nov 27, 2020
bcb41f8
fixed function order
VZout Nov 27, 2020
a5dd089
Update windows.rs
VZout Nov 30, 2020
1ef38f0
Merge branch 'master' into force_theme_v1
VZout Nov 30, 2020
fe258f6
Update CHANGELOG.md
VZout Nov 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,6 +1,7 @@
# Unreleased

- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
- On Windows, add `WindowBuilderExtWindows::with_theme(Theme::)` to set a preferred theme.
VZout marked this conversation as resolved.
Show resolved Hide resolved
- On Windows, fix alt-tab behaviour by removing borderless fullscreen "always on top" flag.
- On Windows, fix bug preventing windows with transparency enabled from having fully-opaque regions.
- **Breaking:** On Windows, include prefix byte in scancodes.
Expand Down
2 changes: 1 addition & 1 deletion FEATURES.md
Expand Up @@ -117,7 +117,7 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting the taskbar icon
* Setting the parent window
* `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme

### macOS
* Window activation policy
Expand Down
17 changes: 13 additions & 4 deletions src/platform/windows.rs
Expand Up @@ -13,7 +13,7 @@ use crate::{
event_loop::EventLoop,
monitor::MonitorHandle,
platform_impl::{EventLoop as WindowsEventLoop, WinIcon},
window::{BadIcon, Icon, Window, WindowBuilder},
window::{BadIcon, Icon, Theme, Window, WindowBuilder},
};

/// Additional methods on `EventLoop` that are specific to Windows.
Expand Down Expand Up @@ -82,7 +82,7 @@ pub trait WindowExtWindows {
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);

/// Whether the system theme is currently Windows 10's "Dark Mode".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation doesn't match anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the comment

fn is_dark_mode(&self) -> bool;
fn theme(&self) -> Theme;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a breaking change, so it should be listed in the changelog

Copy link
Contributor Author

@VZout VZout Nov 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the changelog

}

impl WindowExtWindows for Window {
Expand All @@ -102,8 +102,8 @@ impl WindowExtWindows for Window {
}

#[inline]
fn is_dark_mode(&self) -> bool {
self.window.is_dark_mode()
fn theme(&self) -> Theme {
self.window.theme()
}
}

Expand All @@ -125,6 +125,9 @@ pub trait WindowBuilderExtWindows {
/// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
/// See https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks for more information.
fn with_drag_and_drop(self, flag: bool) -> WindowBuilder;

/// Forces a theme or uses the system settings if `None` was provided.
fn with_theme(self, theme: Option<Theme>) -> WindowBuilder;
}

impl WindowBuilderExtWindows for WindowBuilder {
Expand All @@ -151,6 +154,12 @@ impl WindowBuilderExtWindows for WindowBuilder {
self.platform_specific.drag_and_drop = flag;
self
}

#[inline]
fn with_theme(mut self, theme: Option<Theme>) -> WindowBuilder {
self.platform_specific.preferred_theme = theme;
self
}
}

/// Additional methods on `MonitorHandle` that are specific to Windows.
Expand Down
17 changes: 12 additions & 5 deletions src/platform_impl/windows/dark_mode.rs
Expand Up @@ -14,6 +14,8 @@ use winapi::{
um::{libloaderapi, uxtheme, winuser},
};

use crate::window::Theme;

lazy_static! {
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
// FIXME: RtlGetVersion is a documented windows API,
Expand Down Expand Up @@ -72,9 +74,12 @@ lazy_static! {

/// Attempt to set dark mode on a window, if necessary.
/// Returns true if dark mode was set, false if not.
VZout marked this conversation as resolved.
Show resolved Hide resolved
pub fn try_dark_mode(hwnd: HWND) -> bool {
pub fn try_dark_mode(hwnd: HWND, preferred_theme: Option<Theme>) -> Theme {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually rename this function, since it it's not about dark mode anymore, it's just about trying to get a theme, however when I see try, I'm expecting Result, so I'm not sure if this is a good name. Maybe pick_theme or something like that, if you have better ideas let me know.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if your preferred theme is dark it could theoretically fail on old windows versions. In that case it would return Theme::Light.
So I think try should be included in the name. try_select_theme, try_pick_theme or try_theme 🤷

I don't think it should necessarily return a result. I could change it to a result but it would make the calling code less nice IMO. (It would return a result, if the result is ok the current theme is the preferred theme if the result is a error than the theme is light. Which works but the caller of try_dark_mode needs to understand that the standard theme is light so I prefer to hide it away into the try_ function)

if *DARK_MODE_SUPPORTED {
let is_dark_mode = should_use_dark_mode();
let is_dark_mode = match preferred_theme {
Some(theme) => theme == Theme::Dark,
None => should_use_dark_mode(),
};

let theme_name = if is_dark_mode {
DARK_THEME_NAME.as_ptr()
Expand All @@ -84,10 +89,12 @@ pub fn try_dark_mode(hwnd: HWND) -> bool {

let status = unsafe { uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null()) };

status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode)
} else {
false
if status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode) {
return Theme::Dark;
}
}

Theme::Light
}

fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
Expand Down
26 changes: 13 additions & 13 deletions src/platform_impl/windows/event_loop.rs
Expand Up @@ -45,7 +45,7 @@ use crate::{
window_state::{CursorFlags, WindowFlags, WindowState},
wrap_device_id, WindowId, DEVICE_ID,
},
window::{Fullscreen, WindowId as RootWindowId},
window::{Fullscreen, Theme, WindowId as RootWindowId},
};
use runner::{EventLoopRunner, EventLoopRunnerShared};

Expand Down Expand Up @@ -1874,20 +1874,20 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
winuser::WM_SETTINGCHANGE => {
use crate::event::WindowEvent::ThemeChanged;

let is_dark_mode = try_dark_mode(window);
let mut window_state = subclass_input.window_state.lock();
let changed = window_state.is_dark_mode != is_dark_mode;
let preferred_theme = subclass_input.window_state.lock().preferred_theme;

if changed {
use crate::window::Theme::*;
let theme = if is_dark_mode { Dark } else { Light };
if preferred_theme == None {
let new_theme = try_dark_mode(window, preferred_theme);
let mut window_state = subclass_input.window_state.lock();

window_state.is_dark_mode = is_dark_mode;
mem::drop(window_state);
subclass_input.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: ThemeChanged(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)),
event: ThemeChanged(new_theme),
});
}
}

commctrl::DefSubclassProc(window, msg, wparam, lparam)
Expand Down
3 changes: 3 additions & 0 deletions src/platform_impl/windows/mod.rs
Expand Up @@ -13,13 +13,15 @@ pub use self::icon::WinIcon as PlatformIcon;

use crate::event::DeviceId as RootDeviceId;
use crate::icon::Icon;
use crate::window::Theme;

#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub parent: Option<HWND>,
pub taskbar_icon: Option<Icon>,
pub no_redirection_bitmap: bool,
pub drag_and_drop: bool,
pub preferred_theme: Option<Theme>,
}

impl Default for PlatformSpecificWindowBuilderAttributes {
Expand All @@ -29,6 +31,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
taskbar_icon: None,
no_redirection_bitmap: false,
drag_and_drop: true,
preferred_theme: None,
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/platform_impl/windows/window.rs
Expand Up @@ -43,6 +43,7 @@ use crate::{
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
PlatformSpecificWindowBuilderAttributes, WindowId,
},
window::Theme,
window::{CursorIcon, Fullscreen, WindowAttributes},
};

Expand Down Expand Up @@ -622,8 +623,8 @@ impl Window {
}

#[inline]
pub fn is_dark_mode(&self) -> bool {
self.window_state.lock().is_dark_mode
pub fn theme(&self) -> Theme {
self.window_state.lock().current_theme
}
}

Expand Down Expand Up @@ -733,14 +734,15 @@ unsafe fn init<T: 'static>(
// 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 dark_mode = try_dark_mode(real_window.0);
let current_theme = try_dark_mode(real_window.0, pl_attribs.preferred_theme);

let window_state = {
let window_state = WindowState::new(
&attributes,
pl_attribs.taskbar_icon,
scale_factor,
dark_mode,
current_theme,
pl_attribs.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
11 changes: 7 additions & 4 deletions src/platform_impl/windows/window_state.rs
Expand Up @@ -3,7 +3,7 @@ use crate::{
event::ModifiersState,
icon::Icon,
platform_impl::platform::{event_loop, util},
window::{CursorIcon, Fullscreen, WindowAttributes},
window::{CursorIcon, Fullscreen, Theme, WindowAttributes},
};
use parking_lot::MutexGuard;
use std::{io, ptr};
Expand Down Expand Up @@ -31,7 +31,8 @@ pub struct WindowState {

pub modifiers_state: ModifiersState,
pub fullscreen: Option<Fullscreen>,
pub is_dark_mode: bool,
pub current_theme: Theme,
pub preferred_theme: Option<Theme>,
pub high_surrogate: Option<u16>,
window_flags: WindowFlags,
}
Expand Down Expand Up @@ -101,7 +102,8 @@ impl WindowState {
attributes: &WindowAttributes,
taskbar_icon: Option<Icon>,
scale_factor: f64,
is_dark_mode: bool,
current_theme: Theme,
preferred_theme: Option<Theme>,
) -> WindowState {
WindowState {
mouse: MouseProperties {
Expand All @@ -122,7 +124,8 @@ impl WindowState {

modifiers_state: ModifiersState::default(),
fullscreen: None,
is_dark_mode,
current_theme,
preferred_theme,
high_surrogate: None,
window_flags: WindowFlags::empty(),
}
Expand Down
2 changes: 1 addition & 1 deletion src/window.rs
Expand Up @@ -869,7 +869,7 @@ pub enum Fullscreen {
Borderless(Option<MonitorHandle>),
}

#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Theme {
Light,
Dark,
Expand Down