Skip to content

Commit 9559ba3

Browse files
bashemilk
authored andcommitted
Follow the System Theme in egui (emilk#4860)
* Some initial progress towards emilk#4490 This PR just moves `Theme` and the "follow system theme" settings to egui and adds `RawInput.system_theme`. A follow-up PR can then introduce the two separate `dark_mode_style` and `light_mode_style` fields on `Options`. <!-- Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md) before opening a Pull Request! * If applicable, add a screenshot or gif. * If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example. * Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to test and add commits to your PR. * Remember to run `cargo fmt` and `cargo clippy`. * Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`. * When you have addressed a PR comment, mark it as resolved. Please be patient! I will review your PR, but my time is limited! --> * [x] I have followed the instructions in the PR template ### Breaking changes The options `follow_system_theme` and `default_theme` has been moved from `eframe` into `egui::Options`, settable with `ctx.options_mut` --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
1 parent 3c0c28c commit 9559ba3

File tree

16 files changed

+129
-137
lines changed

16 files changed

+129
-137
lines changed

crates/eframe/src/epi.rs

-62
Original file line numberDiff line numberDiff line change
@@ -297,21 +297,6 @@ pub struct NativeOptions {
297297
#[cfg(any(feature = "glow", feature = "wgpu"))]
298298
pub renderer: Renderer,
299299

300-
/// Try to detect and follow the system preferred setting for dark vs light mode.
301-
///
302-
/// The theme will automatically change when the dark vs light mode preference is changed.
303-
///
304-
/// Does not work on Linux (see <https://github.com/rust-windowing/winit/issues/1549>).
305-
///
306-
/// See also [`Self::default_theme`].
307-
pub follow_system_theme: bool,
308-
309-
/// Which theme to use in case [`Self::follow_system_theme`] is `false`
310-
/// or eframe fails to detect the system theme.
311-
///
312-
/// Default: [`Theme::Dark`].
313-
pub default_theme: Theme,
314-
315300
/// This controls what happens when you close the main eframe window.
316301
///
317302
/// If `true`, execution will continue after the eframe window is closed.
@@ -417,8 +402,6 @@ impl Default for NativeOptions {
417402
#[cfg(any(feature = "glow", feature = "wgpu"))]
418403
renderer: Renderer::default(),
419404

420-
follow_system_theme: cfg!(target_os = "macos") || cfg!(target_os = "windows"),
421-
default_theme: Theme::Dark,
422405
run_and_return: true,
423406

424407
#[cfg(any(feature = "glow", feature = "wgpu"))]
@@ -449,19 +432,6 @@ impl Default for NativeOptions {
449432
/// Options when using `eframe` in a web page.
450433
#[cfg(target_arch = "wasm32")]
451434
pub struct WebOptions {
452-
/// Try to detect and follow the system preferred setting for dark vs light mode.
453-
///
454-
/// See also [`Self::default_theme`].
455-
///
456-
/// Default: `true`.
457-
pub follow_system_theme: bool,
458-
459-
/// Which theme to use in case [`Self::follow_system_theme`] is `false`
460-
/// or system theme detection fails.
461-
///
462-
/// Default: `Theme::Dark`.
463-
pub default_theme: Theme,
464-
465435
/// Sets the number of bits in the depth buffer.
466436
///
467437
/// `egui` doesn't need the depth buffer, so the default value is 0.
@@ -492,8 +462,6 @@ pub struct WebOptions {
492462
impl Default for WebOptions {
493463
fn default() -> Self {
494464
Self {
495-
follow_system_theme: true,
496-
default_theme: Theme::Dark,
497465
depth_buffer: 0,
498466

499467
#[cfg(feature = "glow")]
@@ -509,31 +477,6 @@ impl Default for WebOptions {
509477

510478
// ----------------------------------------------------------------------------
511479

512-
/// Dark or Light theme.
513-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
514-
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
515-
pub enum Theme {
516-
/// Dark mode: light text on a dark background.
517-
Dark,
518-
519-
/// Light mode: dark text on a light background.
520-
Light,
521-
}
522-
523-
impl Theme {
524-
/// Get the egui visuals corresponding to this theme.
525-
///
526-
/// Use with [`egui::Context::set_visuals`].
527-
pub fn egui_visuals(self) -> egui::Visuals {
528-
match self {
529-
Self::Dark => egui::Visuals::dark(),
530-
Self::Light => egui::Visuals::light(),
531-
}
532-
}
533-
}
534-
535-
// ----------------------------------------------------------------------------
536-
537480
/// WebGL Context options
538481
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
539482
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
@@ -814,11 +757,6 @@ pub struct IntegrationInfo {
814757
#[cfg(target_arch = "wasm32")]
815758
pub web_info: WebInfo,
816759

817-
/// Does the OS use dark or light mode?
818-
///
819-
/// `None` means "don't know".
820-
pub system_theme: Option<Theme>,
821-
822760
/// Seconds of cpu usage (in seconds) on the previous frame.
823761
///
824762
/// This includes [`App::update`] as well as rendering (except for vsync waiting).

crates/eframe/src/native/epi_integration.rs

+2-20
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};
1010
use egui::{DeferredViewportUiCallback, NumExt as _, ViewportBuilder, ViewportId};
1111
use egui_winit::{EventResponse, WindowSettings};
1212

13-
use crate::{epi, Theme};
13+
use crate::epi;
1414

1515
pub fn viewport_builder(
1616
egui_zoom_factor: f32,
@@ -158,7 +158,6 @@ pub struct EpiIntegration {
158158
close: bool,
159159

160160
can_drag_window: bool,
161-
follow_system_theme: bool,
162161
#[cfg(feature = "persistence")]
163162
persist_window: bool,
164163
app_icon_setter: super::app_icon::AppTitleIconSetter,
@@ -169,7 +168,6 @@ impl EpiIntegration {
169168
pub fn new(
170169
egui_ctx: egui::Context,
171170
window: &winit::window::Window,
172-
system_theme: Option<Theme>,
173171
app_name: &str,
174172
native_options: &crate::NativeOptions,
175173
storage: Option<Box<dyn epi::Storage>>,
@@ -180,10 +178,7 @@ impl EpiIntegration {
180178
#[cfg(feature = "wgpu")] wgpu_render_state: Option<egui_wgpu::RenderState>,
181179
) -> Self {
182180
let frame = epi::Frame {
183-
info: epi::IntegrationInfo {
184-
system_theme,
185-
cpu_usage: None,
186-
},
181+
info: epi::IntegrationInfo { cpu_usage: None },
187182
storage,
188183
#[cfg(feature = "glow")]
189184
gl,
@@ -217,7 +212,6 @@ impl EpiIntegration {
217212
pending_full_output: Default::default(),
218213
close: false,
219214
can_drag_window: false,
220-
follow_system_theme: native_options.follow_system_theme,
221215
#[cfg(feature = "persistence")]
222216
persist_window: native_options.persist_window,
223217
app_icon_setter,
@@ -251,11 +245,6 @@ impl EpiIntegration {
251245
state: ElementState::Pressed,
252246
..
253247
} => self.can_drag_window = true,
254-
WindowEvent::ThemeChanged(winit_theme) if self.follow_system_theme => {
255-
let theme = theme_from_winit_theme(*winit_theme);
256-
self.frame.info.system_theme = Some(theme);
257-
self.egui_ctx.set_visuals(theme.egui_visuals());
258-
}
259248
_ => {}
260249
}
261250

@@ -398,10 +387,3 @@ pub fn load_egui_memory(_storage: Option<&dyn epi::Storage>) -> Option<egui::Mem
398387
#[cfg(not(feature = "persistence"))]
399388
None
400389
}
401-
402-
pub(crate) fn theme_from_winit_theme(theme: winit::window::Theme) -> Theme {
403-
match theme {
404-
winit::window::Theme::Dark => Theme::Dark,
405-
winit::window::Theme::Light => Theme::Light,
406-
}
407-
}

crates/eframe/src/native/glow_integration.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -228,14 +228,11 @@ impl GlowWinitApp {
228228
}
229229
}
230230

231-
let system_theme =
232-
winit_integration::system_theme(&glutin.window(ViewportId::ROOT), &self.native_options);
233231
let painter = Rc::new(RefCell::new(painter));
234232

235233
let integration = EpiIntegration::new(
236234
egui_ctx,
237235
&glutin.window(ViewportId::ROOT),
238-
system_theme,
239236
&self.app_name,
240237
&self.native_options,
241238
storage,
@@ -281,9 +278,6 @@ impl GlowWinitApp {
281278
}
282279
}
283280

284-
let theme = system_theme.unwrap_or(self.native_options.default_theme);
285-
integration.egui_ctx.set_visuals(theme.egui_visuals());
286-
287281
if self
288282
.native_options
289283
.viewport
@@ -1120,6 +1114,7 @@ impl GlutinWindowContext {
11201114
viewport_id,
11211115
event_loop,
11221116
Some(window.scale_factor() as f32),
1117+
window.theme(),
11231118
self.max_texture_side,
11241119
)
11251120
});

crates/eframe/src/native/wgpu_integration.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,9 @@ impl WgpuWinitApp {
203203

204204
let wgpu_render_state = painter.render_state();
205205

206-
let system_theme = winit_integration::system_theme(&window, &self.native_options);
207206
let integration = EpiIntegration::new(
208207
egui_ctx.clone(),
209208
&window,
210-
system_theme,
211209
&self.app_name,
212210
&self.native_options,
213211
storage,
@@ -243,6 +241,7 @@ impl WgpuWinitApp {
243241
ViewportId::ROOT,
244242
event_loop,
245243
Some(window.scale_factor() as f32),
244+
window.theme(),
246245
painter.max_texture_side(),
247246
);
248247

@@ -251,8 +250,6 @@ impl WgpuWinitApp {
251250
let event_loop_proxy = self.repaint_proxy.lock().clone();
252251
egui_winit.init_accesskit(&window, event_loop_proxy);
253252
}
254-
let theme = system_theme.unwrap_or(self.native_options.default_theme);
255-
egui_ctx.set_visuals(theme.egui_visuals());
256253

257254
let app_creator = std::mem::take(&mut self.app_creator)
258255
.expect("Single-use AppCreator has unexpectedly already been taken");
@@ -872,6 +869,7 @@ impl Viewport {
872869
viewport_id,
873870
event_loop,
874871
Some(window.scale_factor() as f32),
872+
window.theme(),
875873
painter.max_texture_side(),
876874
));
877875

crates/eframe/src/native/winit_integration.rs

-10
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,6 @@ pub enum EventResult {
118118
Exit,
119119
}
120120

121-
pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option<crate::Theme> {
122-
if options.follow_system_theme {
123-
window
124-
.theme()
125-
.map(super::epi_integration::theme_from_winit_theme)
126-
} else {
127-
None
128-
}
129-
}
130-
131121
#[cfg(feature = "accesskit")]
132122
pub(crate) fn on_accesskit_window_event(
133123
egui_winit: &mut egui_winit::State,

crates/eframe/src/web/app_runner.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,11 @@ impl AppRunner {
3838
) -> Result<Self, String> {
3939
let painter = super::ActiveWebPainter::new(canvas, &web_options).await?;
4040

41-
let system_theme = if web_options.follow_system_theme {
42-
super::system_theme()
43-
} else {
44-
None
45-
};
46-
4741
let info = epi::IntegrationInfo {
4842
web_info: epi::WebInfo {
4943
user_agent: super::user_agent().unwrap_or_default(),
5044
location: super::web_location(),
5145
},
52-
system_theme,
5346
cpu_usage: None,
5447
};
5548
let storage = LocalStorage::default();
@@ -68,9 +61,6 @@ impl AppRunner {
6861
o.zoom_factor = 1.0;
6962
});
7063

71-
let theme = system_theme.unwrap_or(web_options.default_theme);
72-
egui_ctx.set_visuals(theme.egui_visuals());
73-
7464
let cc = epi::CreationContext {
7565
egui_ctx: egui_ctx.clone(),
7666
integration_info: info.clone(),
@@ -132,6 +122,7 @@ impl AppRunner {
132122
.entry(egui::ViewportId::ROOT)
133123
.or_default()
134124
.native_pixels_per_point = Some(super::native_pixels_per_point());
125+
runner.input.raw.system_theme = super::system_theme();
135126

136127
Ok(runner)
137128
}

crates/eframe/src/web/events.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub(crate) fn install_event_handlers(runner_ref: &WebRunner) -> Result<(), JsVal
9494
install_wheel(runner_ref, &canvas)?;
9595
install_drag_and_drop(runner_ref, &canvas)?;
9696
install_window_events(runner_ref, &window)?;
97+
install_color_scheme_change_event(runner_ref, &window)?;
9798
Ok(())
9899
}
99100

@@ -353,17 +354,17 @@ fn install_window_events(runner_ref: &WebRunner, window: &EventTarget) -> Result
353354
Ok(())
354355
}
355356

356-
pub(crate) fn install_color_scheme_change_event(runner_ref: &WebRunner) -> Result<(), JsValue> {
357-
let window = web_sys::window().unwrap();
358-
359-
if let Some(media_query_list) = prefers_color_scheme_dark(&window)? {
357+
fn install_color_scheme_change_event(
358+
runner_ref: &WebRunner,
359+
window: &web_sys::Window,
360+
) -> Result<(), JsValue> {
361+
if let Some(media_query_list) = prefers_color_scheme_dark(window)? {
360362
runner_ref.add_event_listener::<web_sys::MediaQueryListEvent>(
361363
&media_query_list,
362364
"change",
363365
|event, runner| {
364366
let theme = theme_from_dark_mode(event.matches());
365-
runner.frame.info.system_theme = Some(theme);
366-
runner.egui_ctx().set_visuals(theme.egui_visuals());
367+
runner.input.raw.system_theme = Some(theme);
367368
runner.needs_repaint.repaint_asap();
368369
},
369370
)?;

crates/eframe/src/web/mod.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ use web_sys::MediaQueryList;
4545

4646
use input::*;
4747

48-
use crate::Theme;
49-
5048
// ----------------------------------------------------------------------------
5149

5250
pub(crate) fn string_from_js_value(value: &JsValue) -> String {
@@ -103,7 +101,7 @@ pub fn native_pixels_per_point() -> f32 {
103101
/// Ask the browser about the preferred system theme.
104102
///
105103
/// `None` means unknown.
106-
pub fn system_theme() -> Option<Theme> {
104+
pub fn system_theme() -> Option<egui::Theme> {
107105
let dark_mode = prefers_color_scheme_dark(&web_sys::window()?)
108106
.ok()??
109107
.matches();
@@ -114,11 +112,11 @@ fn prefers_color_scheme_dark(window: &web_sys::Window) -> Result<Option<MediaQue
114112
window.match_media("(prefers-color-scheme: dark)")
115113
}
116114

117-
fn theme_from_dark_mode(dark_mode: bool) -> Theme {
115+
fn theme_from_dark_mode(dark_mode: bool) -> egui::Theme {
118116
if dark_mode {
119-
Theme::Dark
117+
egui::Theme::Dark
120118
} else {
121-
Theme::Light
119+
egui::Theme::Light
122120
}
123121
}
124122

crates/eframe/src/web/web_runner.rs

-6
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ impl WebRunner {
6363
) -> Result<(), JsValue> {
6464
self.destroy();
6565

66-
let follow_system_theme = web_options.follow_system_theme;
67-
6866
let text_agent = TextAgent::attach(self)?;
6967

7068
let runner = AppRunner::new(canvas, web_options, app_creator, text_agent).await?;
@@ -83,10 +81,6 @@ impl WebRunner {
8381
{
8482
events::install_event_handlers(self)?;
8583

86-
if follow_system_theme {
87-
events::install_color_scheme_change_event(self)?;
88-
}
89-
9084
// The resize observer handles calling `request_animation_frame` to start the render loop.
9185
events::install_resize_observer(self)?;
9286
}

0 commit comments

Comments
 (0)