Skip to content

Commit 92fdf5b

Browse files
authored
Rework theme API
This commit adds support for theming on macOS and also unifies the system theme handling across platforms.
1 parent 4f06cfc commit 92fdf5b

20 files changed

Lines changed: 256 additions & 60 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ And please only add new entries to the top of this list, right below the `# Unre
2222
- On Wayland, a new `wayland-csd-adwaita-crossfont` feature was added to use `crossfont` instead of `ab_glyph` for decorations.
2323
- On Wayland, if not otherwise specified use upstream automatic CSD theme selection.
2424
- On X11, added `WindowExtX11::with_parent` to create child windows.
25+
- Added support for `WindowBuilder::with_theme` and `Window::theme` to support per-window dark/light/system theme configuration on macos, windows and wayland.
26+
- On macOS, added support for `WindowEvent::ThemeChanged`.
27+
- **Breaking:** Removed `WindowBuilderExtWindows::with_theme` and `WindowBuilderExtWayland::with_wayland_csd_theme` in favour of `WindowBuilder::with_theme`.
28+
- **Breaking:** Removed `WindowExtWindows::theme` in favour of `Window::theme`.
2529

2630
# 0.27.4
2731

FEATURES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ If your PR makes notable changes to Winit's features, please update this section
130130
* Hidden titlebar buttons
131131
* Full-size content view
132132
* Accepts first mouse
133+
* Set a preferred theme and get current theme.
133134

134135
### Unix
135136
* Window urgency

examples/theme.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#![allow(clippy::single_match)]
2+
3+
use simple_logger::SimpleLogger;
4+
use winit::{
5+
event::{Event, WindowEvent},
6+
event_loop::{ControlFlow, EventLoop},
7+
window::{Theme, WindowBuilder},
8+
};
9+
10+
fn main() {
11+
SimpleLogger::new().init().unwrap();
12+
let event_loop = EventLoop::new();
13+
14+
let window = WindowBuilder::new()
15+
.with_title("A fantastic window!")
16+
.with_theme(Some(Theme::Dark))
17+
.build(&event_loop)
18+
.unwrap();
19+
20+
println!("Initial theme: {:?}", window.theme());
21+
22+
event_loop.run(move |event, _, control_flow| {
23+
*control_flow = ControlFlow::Wait;
24+
25+
match event {
26+
Event::WindowEvent {
27+
event: WindowEvent::CloseRequested,
28+
..
29+
} => *control_flow = ControlFlow::Exit,
30+
Event::WindowEvent {
31+
event: WindowEvent::ThemeChanged(theme),
32+
window_id,
33+
..
34+
} if window_id == window.id() => {
35+
println!("Theme is changed: {:?}", theme)
36+
}
37+
_ => (),
38+
}
39+
});
40+
}

src/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ pub enum WindowEvent<'a> {
504504
///
505505
/// ## Platform-specific
506506
///
507-
/// At the moment this is only supported on Windows.
507+
/// - **iOS / Android / X11 / Wayland:** Unsupported.
508508
ThemeChanged(Theme),
509509

510510
/// The window has been occluded (completely hidden from view).

src/platform/wayland.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,6 @@ pub trait WindowBuilderExtWayland {
138138
/// For details about application ID conventions, see the
139139
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
140140
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
141-
142-
/// Build window with certain decoration [`Theme`]
143-
///
144-
/// You can also use `WINIT_WAYLAND_CSD_THEME` env variable to set the theme.
145-
/// Possible values for env variable are: "dark" and light".
146-
///
147-
/// When unspecified a theme is automatically selected.
148-
fn with_wayland_csd_theme(self, theme: Theme) -> Self;
149141
}
150142

151143
impl WindowBuilderExtWayland for WindowBuilder {
@@ -154,12 +146,6 @@ impl WindowBuilderExtWayland for WindowBuilder {
154146
self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
155147
self
156148
}
157-
158-
#[inline]
159-
fn with_wayland_csd_theme(mut self, theme: Theme) -> Self {
160-
self.platform_specific.csd_theme = Some(theme);
161-
self
162-
}
163149
}
164150

165151
/// Additional methods on `MonitorHandle` that are specific to Wayland.

src/platform/windows.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
event_loop::EventLoopBuilder,
77
monitor::MonitorHandle,
88
platform_impl::{Parent, WinIcon},
9-
window::{BadIcon, Icon, Theme, Window, WindowBuilder},
9+
window::{BadIcon, Icon, Window, WindowBuilder},
1010
};
1111

1212
/// Window Handle type used by Win32 API
@@ -136,9 +136,6 @@ pub trait WindowExtWindows {
136136
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
137137
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
138138

139-
/// Returns the current window theme.
140-
fn theme(&self) -> Theme;
141-
142139
/// Whether to show or hide the window icon in the taskbar.
143140
fn set_skip_taskbar(&self, skip: bool);
144141

@@ -169,11 +166,6 @@ impl WindowExtWindows for Window {
169166
self.window.set_taskbar_icon(taskbar_icon)
170167
}
171168

172-
#[inline]
173-
fn theme(&self) -> Theme {
174-
self.window.theme()
175-
}
176-
177169
#[inline]
178170
fn set_skip_taskbar(&self, skip: bool) {
179171
self.window.set_skip_taskbar(skip)
@@ -232,9 +224,6 @@ pub trait WindowBuilderExtWindows {
232224
/// See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
233225
fn with_drag_and_drop(self, flag: bool) -> WindowBuilder;
234226

235-
/// Forces a theme or uses the system settings if `None` was provided.
236-
fn with_theme(self, theme: Option<Theme>) -> WindowBuilder;
237-
238227
/// Whether show or hide the window icon in the taskbar.
239228
fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
240229

@@ -282,12 +271,6 @@ impl WindowBuilderExtWindows for WindowBuilder {
282271
self
283272
}
284273

285-
#[inline]
286-
fn with_theme(mut self, theme: Option<Theme>) -> WindowBuilder {
287-
self.platform_specific.preferred_theme = theme;
288-
self
289-
}
290-
291274
#[inline]
292275
fn with_skip_taskbar(mut self, skip: bool) -> WindowBuilder {
293276
self.platform_specific.skip_taskbar = skip;

src/platform_impl/android/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::{
2424
error,
2525
event::{self, VirtualKeyCode},
2626
event_loop::{self, ControlFlow},
27-
window::{self, CursorGrabMode},
27+
window::{self, CursorGrabMode, Theme},
2828
};
2929

3030
static CONFIG: Lazy<RwLock<Configuration>> = Lazy::new(|| {
@@ -852,6 +852,10 @@ impl Window {
852852
pub fn content_rect(&self) -> Rect {
853853
ndk_glue::content_rect()
854854
}
855+
856+
pub fn theme(&self) -> Option<Theme> {
857+
None
858+
}
855859
}
856860

857861
#[derive(Default, Clone, Debug)]

src/platform_impl/ios/window.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use crate::{
2323
monitor, view, EventLoopWindowTarget, Fullscreen, MonitorHandle,
2424
},
2525
window::{
26-
CursorGrabMode, CursorIcon, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
26+
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes,
27+
WindowId as RootWindowId,
2728
},
2829
};
2930

@@ -341,6 +342,11 @@ impl Inner {
341342
pub fn raw_display_handle(&self) -> RawDisplayHandle {
342343
RawDisplayHandle::UiKit(UiKitDisplayHandle::empty())
343344
}
345+
346+
pub fn theme(&self) -> Option<Theme> {
347+
warn!("`Window::theme` is ignored on iOS");
348+
None
349+
}
344350
}
345351

346352
pub struct Window {

src/platform_impl/linux/mod.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ pub use self::x11::XNotSupported;
3131
use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError};
3232
#[cfg(feature = "x11")]
3333
use crate::platform::x11::XlibErrorHook;
34-
#[cfg(feature = "wayland")]
35-
use crate::window::Theme;
3634
use crate::{
3735
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
3836
error::{ExternalError, NotSupportedError, OsError as RootOsError},
@@ -41,7 +39,7 @@ use crate::{
4139
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
4240
},
4341
icon::Icon,
44-
window::{CursorGrabMode, CursorIcon, UserAttentionType, WindowAttributes},
42+
window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes},
4543
};
4644

4745
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
@@ -104,8 +102,6 @@ pub struct PlatformSpecificWindowBuilderAttributes {
104102
pub x11_window_types: Vec<XWindowType>,
105103
#[cfg(feature = "x11")]
106104
pub gtk_theme_variant: Option<String>,
107-
#[cfg(feature = "wayland")]
108-
pub csd_theme: Option<Theme>,
109105
}
110106

111107
impl Default for PlatformSpecificWindowBuilderAttributes {
@@ -126,8 +122,6 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
126122
x11_window_types: vec![XWindowType::Normal],
127123
#[cfg(feature = "x11")]
128124
gtk_theme_variant: None,
129-
#[cfg(feature = "wayland")]
130-
csd_theme: None,
131125
}
132126
}
133127
}
@@ -590,6 +584,11 @@ impl Window {
590584
pub fn raw_display_handle(&self) -> RawDisplayHandle {
591585
x11_or_wayland!(match self; Window(window) => window.raw_display_handle())
592586
}
587+
588+
#[inline]
589+
pub fn theme(&self) -> Option<Theme> {
590+
x11_or_wayland!(match self; Window(window) => window.theme())
591+
}
593592
}
594593

595594
/// Hooks for X11 errors.

src/platform_impl/linux/wayland/window/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ impl Window {
170170
// Set CSD frame config from theme if specified,
171171
// otherwise use upstream automatic selection.
172172
#[cfg(feature = "sctk-adwaita")]
173-
if let Some(theme) = platform_attributes.csd_theme.or_else(|| {
173+
if let Some(theme) = attributes.preferred_theme.or_else(|| {
174174
std::env::var(WAYLAND_CSD_THEME_ENV_VAR)
175175
.ok()
176176
.and_then(|s| s.as_str().try_into().ok())
@@ -619,6 +619,11 @@ impl Window {
619619
self.window_requests.lock().unwrap().push(request);
620620
self.event_loop_awakener.ping();
621621
}
622+
623+
#[inline]
624+
pub fn theme(&self) -> Option<Theme> {
625+
None
626+
}
622627
}
623628

624629
impl Drop for Window {

0 commit comments

Comments
 (0)