mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 13:31:29 +11:00
Add support for Windows Dark Mode (#1217)
* Add support for Windows Dark Mode * Add is_dark_mode() getter to WindowExtWindows * Add WindowEvent::DarkModeChanged * Add support for dark mode in Windows 10 builds > 18362 * Change strategy for querying windows 10 build version * Drop window state before sending event Co-Authored-By: daxpedda <daxpedda@gmail.com> * Change implementation of windows dark mode support * Expand supported range of windows 10 versions with dark mode * Use get_function! macro where possible * Minor style fixes * Improve documentation for ThemeChanged * Use `as` conversion for `BOOL` * Correct CHANGELOG entry for dark mode Co-authored-by: daxpedda <daxpedda@gmail.com> Co-authored-by: Osspial <osspial@gmail.com>
This commit is contained in:
parent
25e018d1ce
commit
d59eec4633
|
@ -6,6 +6,8 @@
|
||||||
- On macOS, fix application not to terminate on `run_return`.
|
- On macOS, fix application not to terminate on `run_return`.
|
||||||
- On Wayland, fix cursor icon updates on window borders when using CSD.
|
- On Wayland, fix cursor icon updates on window borders when using CSD.
|
||||||
- On Wayland, under mutter(GNOME Wayland), fix CSD being behind the status bar, when starting window in maximized mode.
|
- On Wayland, under mutter(GNOME Wayland), fix CSD being behind the status bar, when starting window in maximized mode.
|
||||||
|
- On Windows, theme the title bar according to whether the system theme is "Light" or "Dark".
|
||||||
|
- Added `WindowEvent::ThemeChanged` variant to handle changes to the system theme. Currently only implemented on Windows.
|
||||||
- Changes to the `RedrawRequested` event (#1041):
|
- Changes to the `RedrawRequested` event (#1041):
|
||||||
- `RedrawRequested` has been moved from `WindowEvent` to `Event`.
|
- `RedrawRequested` has been moved from `WindowEvent` to `Event`.
|
||||||
- `EventsCleared` has been renamed to `MainEventsCleared`.
|
- `EventsCleared` has been renamed to `MainEventsCleared`.
|
||||||
|
|
|
@ -117,6 +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
|
||||||
|
|
||||||
### macOS
|
### macOS
|
||||||
* Window activation policy
|
* Window activation policy
|
||||||
|
|
10
src/event.rs
10
src/event.rs
|
@ -10,7 +10,7 @@ use std::path::PathBuf;
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{LogicalPosition, LogicalSize},
|
dpi::{LogicalPosition, LogicalSize},
|
||||||
platform_impl,
|
platform_impl,
|
||||||
window::WindowId,
|
window::{Theme, WindowId},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes a generic event.
|
/// Describes a generic event.
|
||||||
|
@ -222,6 +222,14 @@ pub enum WindowEvent {
|
||||||
///
|
///
|
||||||
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
|
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
|
||||||
HiDpiFactorChanged(f64),
|
HiDpiFactorChanged(f64),
|
||||||
|
|
||||||
|
/// The system window theme has changed.
|
||||||
|
///
|
||||||
|
/// Applications might wish to react to this to change the theme of the content of the window
|
||||||
|
/// when the system changes the window theme.
|
||||||
|
///
|
||||||
|
/// At the moment this is only supported on Windows.
|
||||||
|
ThemeChanged(Theme),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identifier of an input device.
|
/// Identifier of an input device.
|
||||||
|
|
|
@ -77,6 +77,9 @@ 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".
|
||||||
|
fn is_dark_mode(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowExtWindows for Window {
|
impl WindowExtWindows for Window {
|
||||||
|
@ -94,6 +97,11 @@ impl WindowExtWindows for Window {
|
||||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||||
self.window.set_taskbar_icon(taskbar_icon)
|
self.window.set_taskbar_icon(taskbar_icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_dark_mode(&self) -> bool {
|
||||||
|
self.window.is_dark_mode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||||
|
|
216
src/platform_impl/windows/dark_mode.rs
Normal file
216
src/platform_impl/windows/dark_mode.rs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
/// 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 std::ffi::OsStr;
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
|
||||||
|
use winapi::{
|
||||||
|
shared::{
|
||||||
|
basetsd::SIZE_T,
|
||||||
|
minwindef::{BOOL, DWORD, UINT, ULONG, WORD},
|
||||||
|
ntdef::{LPSTR, NTSTATUS, NT_SUCCESS, PVOID, WCHAR},
|
||||||
|
windef::HWND,
|
||||||
|
},
|
||||||
|
um::{libloaderapi, uxtheme, winuser},
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
|
||||||
|
// FIXME: RtlGetVersion is a documented windows API,
|
||||||
|
// should be part of winapi!
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct OSVERSIONINFOW {
|
||||||
|
dwOSVersionInfoSize: ULONG,
|
||||||
|
dwMajorVersion: ULONG,
|
||||||
|
dwMinorVersion: ULONG,
|
||||||
|
dwBuildNumber: ULONG,
|
||||||
|
dwPlatformId: ULONG,
|
||||||
|
szCSDVersion: [WCHAR; 128],
|
||||||
|
}
|
||||||
|
|
||||||
|
type RtlGetVersion = unsafe extern "system" fn (*mut OSVERSIONINFOW) -> NTSTATUS;
|
||||||
|
let handle = get_function!("ntdll.dll", RtlGetVersion);
|
||||||
|
|
||||||
|
if let Some(rtl_get_version) = handle {
|
||||||
|
unsafe {
|
||||||
|
let mut vi = OSVERSIONINFOW {
|
||||||
|
dwOSVersionInfoSize: 0,
|
||||||
|
dwMajorVersion: 0,
|
||||||
|
dwMinorVersion: 0,
|
||||||
|
dwBuildNumber: 0,
|
||||||
|
dwPlatformId: 0,
|
||||||
|
szCSDVersion: [0; 128],
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = (rtl_get_version)(&mut vi as _);
|
||||||
|
assert!(NT_SUCCESS(status));
|
||||||
|
|
||||||
|
if vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
|
||||||
|
Some(vi.dwBuildNumber)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static ref DARK_MODE_SUPPORTED: bool = {
|
||||||
|
// We won't try to do anything for windows versions < 17763
|
||||||
|
// (Windows 10 October 2018 update)
|
||||||
|
match *WIN10_BUILD_VERSION {
|
||||||
|
Some(v) => v >= 17763,
|
||||||
|
None => false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static ref DARK_THEME_NAME: Vec<u16> = widestring("DarkMode_Explorer");
|
||||||
|
static ref LIGHT_THEME_NAME: Vec<u16> = widestring("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to set dark mode on a window, if necessary.
|
||||||
|
/// Returns true if dark mode was set, false if not.
|
||||||
|
pub fn try_dark_mode(hwnd: HWND) -> bool {
|
||||||
|
if *DARK_MODE_SUPPORTED {
|
||||||
|
let is_dark_mode = should_use_dark_mode();
|
||||||
|
|
||||||
|
let theme_name = if is_dark_mode {
|
||||||
|
DARK_THEME_NAME.as_ptr()
|
||||||
|
} else {
|
||||||
|
LIGHT_THEME_NAME.as_ptr()
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(
|
||||||
|
0,
|
||||||
|
uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null())
|
||||||
|
);
|
||||||
|
|
||||||
|
set_dark_mode_for_window(hwnd, is_dark_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
is_dark_mode
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) {
|
||||||
|
// Uses Windows undocumented API SetWindowCompositionAttribute,
|
||||||
|
// as seen in win32-darkmode example linked at top of file.
|
||||||
|
|
||||||
|
type SetWindowCompositionAttribute =
|
||||||
|
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
type WINDOWCOMPOSITIONATTRIB = u32;
|
||||||
|
const WCA_USEDARKMODECOLORS: WINDOWCOMPOSITIONATTRIB = 26;
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct WINDOWCOMPOSITIONATTRIBDATA {
|
||||||
|
Attrib: WINDOWCOMPOSITIONATTRIB,
|
||||||
|
pvData: PVOID,
|
||||||
|
cbData: SIZE_T,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref SET_WINDOW_COMPOSITION_ATTRIBUTE: Option<SetWindowCompositionAttribute> =
|
||||||
|
get_function!("user32.dll", SetWindowCompositionAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE {
|
||||||
|
unsafe {
|
||||||
|
// SetWindowCompositionAttribute needs a bigbool (i32), not bool.
|
||||||
|
let mut is_dark_mode_bigbool = is_dark_mode as BOOL;
|
||||||
|
|
||||||
|
let mut data = WINDOWCOMPOSITIONATTRIBDATA {
|
||||||
|
Attrib: WCA_USEDARKMODECOLORS,
|
||||||
|
pvData: &mut is_dark_mode_bigbool as *mut _ as _,
|
||||||
|
cbData: std::mem::size_of_val(&is_dark_mode_bigbool) as _,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
1,
|
||||||
|
set_window_composition_attribute(hwnd, &mut data as *mut _)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_use_dark_mode() -> bool {
|
||||||
|
should_apps_use_dark_mode() && !is_high_contrast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_apps_use_dark_mode() -> bool {
|
||||||
|
type ShouldAppsUseDarkMode = unsafe extern "system" fn() -> bool;
|
||||||
|
lazy_static! {
|
||||||
|
static ref SHOULD_APPS_USE_DARK_MODE: Option<ShouldAppsUseDarkMode> = {
|
||||||
|
unsafe {
|
||||||
|
const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: WORD = 132;
|
||||||
|
|
||||||
|
let module = libloaderapi::LoadLibraryA("uxtheme.dll\0".as_ptr() as _);
|
||||||
|
|
||||||
|
if module.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let handle = libloaderapi::GetProcAddress(
|
||||||
|
module,
|
||||||
|
winuser::MAKEINTRESOURCEA(UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL),
|
||||||
|
);
|
||||||
|
|
||||||
|
if handle.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(std::mem::transmute(handle))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOULD_APPS_USE_DARK_MODE
|
||||||
|
.map(|should_apps_use_dark_mode| unsafe { (should_apps_use_dark_mode)() })
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This definition was missing from winapi. Can remove from
|
||||||
|
// here and use winapi once the following PR is released:
|
||||||
|
// https://github.com/retep998/winapi-rs/pull/815
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct HIGHCONTRASTA {
|
||||||
|
cbSize: UINT,
|
||||||
|
dwFlags: DWORD,
|
||||||
|
lpszDefaultScheme: LPSTR,
|
||||||
|
}
|
||||||
|
|
||||||
|
const HCF_HIGHCONTRASTON: DWORD = 1;
|
||||||
|
|
||||||
|
fn is_high_contrast() -> bool {
|
||||||
|
let mut hc = HIGHCONTRASTA {
|
||||||
|
cbSize: 0,
|
||||||
|
dwFlags: 0,
|
||||||
|
lpszDefaultScheme: std::ptr::null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ok = unsafe {
|
||||||
|
winuser::SystemParametersInfoA(
|
||||||
|
winuser::SPI_GETHIGHCONTRAST,
|
||||||
|
std::mem::size_of_val(&hc) as _,
|
||||||
|
&mut hc as *mut _ as _,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
(ok > 0) && ((HCF_HIGHCONTRASTON & hc.dwFlags) == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn widestring(src: &'static str) -> Vec<u16> {
|
||||||
|
OsStr::new(src)
|
||||||
|
.encode_wide()
|
||||||
|
.chain(Some(0).into_iter())
|
||||||
|
.collect()
|
||||||
|
}
|
|
@ -48,6 +48,7 @@ use crate::{
|
||||||
event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent},
|
event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
|
dark_mode::try_dark_mode,
|
||||||
dpi::{
|
dpi::{
|
||||||
become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_scale_factor,
|
become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_scale_factor,
|
||||||
},
|
},
|
||||||
|
@ -1540,6 +1541,28 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
use crate::window::Theme::*;
|
||||||
|
let theme = if is_dark_mode { Dark } else { Light };
|
||||||
|
|
||||||
|
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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
if msg == *DESTROY_MSG_ID {
|
if msg == *DESTROY_MSG_ID {
|
||||||
winuser::DestroyWindow(window);
|
winuser::DestroyWindow(window);
|
||||||
|
|
|
@ -69,6 +69,7 @@ impl WindowId {
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod util;
|
mod util;
|
||||||
|
mod dark_mode;
|
||||||
mod dpi;
|
mod dpi;
|
||||||
mod drop_handler;
|
mod drop_handler;
|
||||||
mod event;
|
mod event;
|
||||||
|
|
|
@ -34,6 +34,7 @@ use crate::{
|
||||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||||
monitor::MonitorHandle as RootMonitorHandle,
|
monitor::MonitorHandle as RootMonitorHandle,
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
|
dark_mode::try_dark_mode,
|
||||||
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, INITIAL_DPI_MSG_ID},
|
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID},
|
||||||
|
@ -696,6 +697,11 @@ impl Window {
|
||||||
pub fn set_ime_position(&self, _logical_spot: LogicalPosition) {
|
pub fn set_ime_position(&self, _logical_spot: LogicalPosition) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_dark_mode(&self) -> bool {
|
||||||
|
self.window_state.lock().is_dark_mode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Window {
|
impl Drop for Window {
|
||||||
|
@ -903,8 +909,19 @@ unsafe fn init<T: 'static>(
|
||||||
window_flags.set(WindowFlags::VISIBLE, attributes.visible);
|
window_flags.set(WindowFlags::VISIBLE, attributes.visible);
|
||||||
window_flags.set(WindowFlags::MAXIMIZED, attributes.maximized);
|
window_flags.set(WindowFlags::MAXIMIZED, attributes.maximized);
|
||||||
|
|
||||||
|
// 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 window_state = {
|
let window_state = {
|
||||||
let window_state = WindowState::new(&attributes, window_icon, taskbar_icon, dpi_factor);
|
let window_state = WindowState::new(
|
||||||
|
&attributes,
|
||||||
|
window_icon,
|
||||||
|
taskbar_icon,
|
||||||
|
dpi_factor,
|
||||||
|
dark_mode,
|
||||||
|
);
|
||||||
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);
|
||||||
window_state
|
window_state
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub struct WindowState {
|
||||||
/// Used to supress duplicate redraw attempts when calling `request_redraw` multiple
|
/// Used to supress duplicate redraw attempts when calling `request_redraw` multiple
|
||||||
/// times in `EventsCleared`.
|
/// times in `EventsCleared`.
|
||||||
pub queued_out_of_band_redraw: bool,
|
pub queued_out_of_band_redraw: bool,
|
||||||
|
pub is_dark_mode: bool,
|
||||||
pub high_surrogate: Option<u16>,
|
pub high_surrogate: Option<u16>,
|
||||||
window_flags: WindowFlags,
|
window_flags: WindowFlags,
|
||||||
}
|
}
|
||||||
|
@ -98,6 +99,7 @@ impl WindowState {
|
||||||
window_icon: Option<WinIcon>,
|
window_icon: Option<WinIcon>,
|
||||||
taskbar_icon: Option<WinIcon>,
|
taskbar_icon: Option<WinIcon>,
|
||||||
dpi_factor: f64,
|
dpi_factor: f64,
|
||||||
|
is_dark_mode: bool,
|
||||||
) -> WindowState {
|
) -> WindowState {
|
||||||
WindowState {
|
WindowState {
|
||||||
mouse: MouseProperties {
|
mouse: MouseProperties {
|
||||||
|
@ -117,6 +119,7 @@ impl WindowState {
|
||||||
|
|
||||||
fullscreen: None,
|
fullscreen: None,
|
||||||
queued_out_of_band_redraw: false,
|
queued_out_of_band_redraw: false,
|
||||||
|
is_dark_mode,
|
||||||
high_surrogate: None,
|
high_surrogate: None,
|
||||||
window_flags: WindowFlags::empty(),
|
window_flags: WindowFlags::empty(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -857,3 +857,9 @@ pub enum Fullscreen {
|
||||||
Exclusive(VideoMode),
|
Exclusive(VideoMode),
|
||||||
Borderless(MonitorHandle),
|
Borderless(MonitorHandle),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Theme {
|
||||||
|
Light,
|
||||||
|
Dark,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue