mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 13:31:29 +11:00
Ability to force a theme on Windows (#1666)
This commit is contained in:
parent
5700359a61
commit
6ddee9a8ac
|
@ -1,5 +1,8 @@
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
- **Breaking:** On Windows, Renamed `WindowBuilderExtWindows`'s `is_dark_mode` to `theme`.
|
||||||
|
- On Windows, add `WindowBuilderExtWindows::with_theme` to set a preferred theme.
|
||||||
|
- On Windows, fix bug causing message boxes to appear delayed.
|
||||||
- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
|
- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
|
||||||
- On Windows, fix alt-tab behaviour by removing borderless fullscreen "always on top" flag.
|
- 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.
|
- On Windows, fix bug preventing windows with transparency enabled from having fully-opaque regions.
|
||||||
|
|
|
@ -117,7 +117,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||||
* Setting the taskbar icon
|
* Setting the taskbar icon
|
||||||
* Setting the parent window
|
* Setting the parent window
|
||||||
* `WS_EX_NOREDIRECTIONBITMAP` support
|
* `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
|
### macOS
|
||||||
* Window activation policy
|
* Window activation policy
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
monitor::MonitorHandle,
|
monitor::MonitorHandle,
|
||||||
platform_impl::{EventLoop as WindowsEventLoop, WinIcon},
|
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.
|
/// Additional methods on `EventLoop` that are specific to Windows.
|
||||||
|
@ -81,8 +81,8 @@ pub trait WindowExtWindows {
|
||||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
|
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
|
||||||
|
|
||||||
/// Whether the system theme is currently Windows 10's "Dark Mode".
|
/// Returns the current window theme.
|
||||||
fn is_dark_mode(&self) -> bool;
|
fn theme(&self) -> Theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowExtWindows for Window {
|
impl WindowExtWindows for Window {
|
||||||
|
@ -102,8 +102,8 @@ impl WindowExtWindows for Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_dark_mode(&self) -> bool {
|
fn theme(&self) -> Theme {
|
||||||
self.window.is_dark_mode()
|
self.window.theme()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +125,9 @@ pub trait WindowBuilderExtWindows {
|
||||||
/// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
|
/// 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.
|
/// 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;
|
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 {
|
impl WindowBuilderExtWindows for WindowBuilder {
|
||||||
|
@ -151,6 +154,12 @@ impl WindowBuilderExtWindows for WindowBuilder {
|
||||||
self.platform_specific.drag_and_drop = flag;
|
self.platform_specific.drag_and_drop = flag;
|
||||||
self
|
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.
|
/// Additional methods on `MonitorHandle` that are specific to Windows.
|
||||||
|
|
|
@ -14,6 +14,8 @@ use winapi::{
|
||||||
um::{libloaderapi, uxtheme, winuser},
|
um::{libloaderapi, uxtheme, winuser},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::window::Theme;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
|
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
|
||||||
// FIXME: RtlGetVersion is a documented windows API,
|
// FIXME: RtlGetVersion is a documented windows API,
|
||||||
|
@ -70,11 +72,14 @@ lazy_static! {
|
||||||
static ref LIGHT_THEME_NAME: Vec<u16> = widestring("");
|
static ref LIGHT_THEME_NAME: Vec<u16> = widestring("");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to set dark mode on a window, if necessary.
|
/// Attempt to set a theme on a window, if necessary.
|
||||||
/// Returns true if dark mode was set, false if not.
|
/// Returns the theme that was picked
|
||||||
pub fn try_dark_mode(hwnd: HWND) -> bool {
|
pub fn try_theme(hwnd: HWND, preferred_theme: Option<Theme>) -> Theme {
|
||||||
if *DARK_MODE_SUPPORTED {
|
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 {
|
let theme_name = if is_dark_mode {
|
||||||
DARK_THEME_NAME.as_ptr()
|
DARK_THEME_NAME.as_ptr()
|
||||||
|
@ -84,10 +89,12 @@ pub fn try_dark_mode(hwnd: HWND) -> bool {
|
||||||
|
|
||||||
let status = unsafe { uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null()) };
|
let status = unsafe { uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null()) };
|
||||||
|
|
||||||
status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode)
|
if status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode) {
|
||||||
} else {
|
return Theme::Dark;
|
||||||
false
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Theme::Light
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
|
fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
|
||||||
|
|
|
@ -36,7 +36,7 @@ use crate::{
|
||||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||||
monitor::MonitorHandle as RootMonitorHandle,
|
monitor::MonitorHandle as RootMonitorHandle,
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
dark_mode::try_dark_mode,
|
dark_mode::try_theme,
|
||||||
dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling},
|
dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling},
|
||||||
drop_handler::FileDropHandler,
|
drop_handler::FileDropHandler,
|
||||||
event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey},
|
event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey},
|
||||||
|
@ -1874,20 +1874,20 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||||
winuser::WM_SETTINGCHANGE => {
|
winuser::WM_SETTINGCHANGE => {
|
||||||
use crate::event::WindowEvent::ThemeChanged;
|
use crate::event::WindowEvent::ThemeChanged;
|
||||||
|
|
||||||
let is_dark_mode = try_dark_mode(window);
|
let preferred_theme = subclass_input.window_state.lock().preferred_theme;
|
||||||
let mut window_state = subclass_input.window_state.lock();
|
|
||||||
let changed = window_state.is_dark_mode != is_dark_mode;
|
|
||||||
|
|
||||||
if changed {
|
if preferred_theme == None {
|
||||||
use crate::window::Theme::*;
|
let new_theme = try_theme(window, preferred_theme);
|
||||||
let theme = if is_dark_mode { Dark } else { Light };
|
let mut window_state = subclass_input.window_state.lock();
|
||||||
|
|
||||||
window_state.is_dark_mode = is_dark_mode;
|
if window_state.current_theme != new_theme {
|
||||||
mem::drop(window_state);
|
window_state.current_theme = new_theme;
|
||||||
subclass_input.send_event(Event::WindowEvent {
|
mem::drop(window_state);
|
||||||
window_id: RootWindowId(WindowId(window)),
|
subclass_input.send_event(Event::WindowEvent {
|
||||||
event: ThemeChanged(theme),
|
window_id: RootWindowId(WindowId(window)),
|
||||||
});
|
event: ThemeChanged(new_theme),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub use self::icon::WinIcon as PlatformIcon;
|
||||||
|
|
||||||
use crate::event::DeviceId as RootDeviceId;
|
use crate::event::DeviceId as RootDeviceId;
|
||||||
use crate::icon::Icon;
|
use crate::icon::Icon;
|
||||||
|
use crate::window::Theme;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
|
@ -20,6 +21,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
pub taskbar_icon: Option<Icon>,
|
pub taskbar_icon: Option<Icon>,
|
||||||
pub no_redirection_bitmap: bool,
|
pub no_redirection_bitmap: bool,
|
||||||
pub drag_and_drop: bool,
|
pub drag_and_drop: bool,
|
||||||
|
pub preferred_theme: Option<Theme>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PlatformSpecificWindowBuilderAttributes {
|
impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||||
|
@ -29,6 +31,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||||
taskbar_icon: None,
|
taskbar_icon: None,
|
||||||
no_redirection_bitmap: false,
|
no_redirection_bitmap: false,
|
||||||
drag_and_drop: true,
|
drag_and_drop: true,
|
||||||
|
preferred_theme: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ use crate::{
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
monitor::MonitorHandle as RootMonitorHandle,
|
monitor::MonitorHandle as RootMonitorHandle,
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
dark_mode::try_dark_mode,
|
dark_mode::try_theme,
|
||||||
dpi::{dpi_to_scale_factor, hwnd_dpi},
|
dpi::{dpi_to_scale_factor, hwnd_dpi},
|
||||||
drop_handler::FileDropHandler,
|
drop_handler::FileDropHandler,
|
||||||
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
||||||
|
@ -43,7 +43,7 @@ use crate::{
|
||||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||||
PlatformSpecificWindowBuilderAttributes, WindowId,
|
PlatformSpecificWindowBuilderAttributes, WindowId,
|
||||||
},
|
},
|
||||||
window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes},
|
window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The Win32 implementation of the main `Window` object.
|
/// The Win32 implementation of the main `Window` object.
|
||||||
|
@ -653,8 +653,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_dark_mode(&self) -> bool {
|
pub fn theme(&self) -> Theme {
|
||||||
self.window_state.lock().is_dark_mode
|
self.window_state.lock().current_theme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -764,14 +764,15 @@ unsafe fn init<T: 'static>(
|
||||||
// If the system theme is dark, we need to set the window theme now
|
// If the system theme is dark, we need to set the window theme now
|
||||||
// before we update the window flags (and possibly show the
|
// before we update the window flags (and possibly show the
|
||||||
// window for the first time).
|
// window for the first time).
|
||||||
let dark_mode = try_dark_mode(real_window.0);
|
let current_theme = try_theme(real_window.0, pl_attribs.preferred_theme);
|
||||||
|
|
||||||
let window_state = {
|
let window_state = {
|
||||||
let window_state = WindowState::new(
|
let window_state = WindowState::new(
|
||||||
&attributes,
|
&attributes,
|
||||||
pl_attribs.taskbar_icon,
|
pl_attribs.taskbar_icon,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
dark_mode,
|
current_theme,
|
||||||
|
pl_attribs.preferred_theme,
|
||||||
);
|
);
|
||||||
let window_state = Arc::new(Mutex::new(window_state));
|
let window_state = Arc::new(Mutex::new(window_state));
|
||||||
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
|
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
event::ModifiersState,
|
event::ModifiersState,
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
platform_impl::platform::{event_loop, util},
|
platform_impl::platform::{event_loop, util},
|
||||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
window::{CursorIcon, Fullscreen, Theme, WindowAttributes},
|
||||||
};
|
};
|
||||||
use parking_lot::MutexGuard;
|
use parking_lot::MutexGuard;
|
||||||
use std::{io, ptr};
|
use std::{io, ptr};
|
||||||
|
@ -31,7 +31,8 @@ pub struct WindowState {
|
||||||
|
|
||||||
pub modifiers_state: ModifiersState,
|
pub modifiers_state: ModifiersState,
|
||||||
pub fullscreen: Option<Fullscreen>,
|
pub fullscreen: Option<Fullscreen>,
|
||||||
pub is_dark_mode: bool,
|
pub current_theme: Theme,
|
||||||
|
pub preferred_theme: Option<Theme>,
|
||||||
pub high_surrogate: Option<u16>,
|
pub high_surrogate: Option<u16>,
|
||||||
window_flags: WindowFlags,
|
window_flags: WindowFlags,
|
||||||
}
|
}
|
||||||
|
@ -101,7 +102,8 @@ impl WindowState {
|
||||||
attributes: &WindowAttributes,
|
attributes: &WindowAttributes,
|
||||||
taskbar_icon: Option<Icon>,
|
taskbar_icon: Option<Icon>,
|
||||||
scale_factor: f64,
|
scale_factor: f64,
|
||||||
is_dark_mode: bool,
|
current_theme: Theme,
|
||||||
|
preferred_theme: Option<Theme>,
|
||||||
) -> WindowState {
|
) -> WindowState {
|
||||||
WindowState {
|
WindowState {
|
||||||
mouse: MouseProperties {
|
mouse: MouseProperties {
|
||||||
|
@ -122,7 +124,8 @@ impl WindowState {
|
||||||
|
|
||||||
modifiers_state: ModifiersState::default(),
|
modifiers_state: ModifiersState::default(),
|
||||||
fullscreen: None,
|
fullscreen: None,
|
||||||
is_dark_mode,
|
current_theme,
|
||||||
|
preferred_theme,
|
||||||
high_surrogate: None,
|
high_surrogate: None,
|
||||||
window_flags: WindowFlags::empty(),
|
window_flags: WindowFlags::empty(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -886,7 +886,7 @@ pub enum Fullscreen {
|
||||||
Borderless(Option<MonitorHandle>),
|
Borderless(Option<MonitorHandle>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Theme {
|
pub enum Theme {
|
||||||
Light,
|
Light,
|
||||||
Dark,
|
Dark,
|
||||||
|
|
Loading…
Reference in a new issue