From 9cbe0a02c6e2e77290cc9fa3b18dbacfb25b8a6d Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 9 Jun 2022 16:37:52 +0200 Subject: [PATCH] Use dark-light on Mac and Windows dark-light has a nasty problem on Linux: https://github.com/frewsxcv/rust-dark-light/issues/17 So we made dark-light opt-in in https://github.com/emilk/egui/pull/1437 This PR makes dark-light a default dependency again, but only use it on Max and Windows. This is controlled with the new NativeOptions::follow_system_theme. If this isn't enabled, then NativeOptions::default_theme is used. --- eframe/Cargo.toml | 4 +- eframe/src/epi.rs | 65 +++++++++++++++++++++++++++- eframe/src/native/epi_integration.rs | 33 ++------------ eframe/src/native/run.rs | 14 ++++++ egui_demo_app/src/wrap_app.rs | 6 --- 5 files changed, 84 insertions(+), 38 deletions(-) diff --git a/eframe/Cargo.toml b/eframe/Cargo.toml index 4ff1f94f469..cc06e204677 100644 --- a/eframe/Cargo.toml +++ b/eframe/Cargo.toml @@ -20,9 +20,11 @@ all-features = true [features] -default = ["default_fonts", "glow"] +default = ["dark-light", "default_fonts", "glow"] ## Detect dark mode system preference using [`dark-light`](https://docs.rs/dark-light). +## +## See also [`NativeOptions::follow_system_theme`] and [`NativeOptions::default_theme`]. dark-light = ["dep:dark-light"] ## If set, egui will use `include_bytes!` to bundle some fonts. diff --git a/eframe/src/epi.rs b/eframe/src/epi.rs index aecb78912d1..46d701f840e 100644 --- a/eframe/src/epi.rs +++ b/eframe/src/epi.rs @@ -243,6 +243,23 @@ pub struct NativeOptions { /// What rendering backend to use. pub renderer: Renderer, + + /// If the `dark-light` feature is enabled: + /// + /// Try to detect and follow the system preferred setting for dark vs light mode. + /// + /// By default, this is `true` on Mac and Windows, but `false` on Linux + /// due to . + /// + /// See also [`Self::default_theme`]. + pub follow_system_theme: bool, + + /// Use the dark mode theme if: + /// * the `dark-light` feature is disabled + /// * OR [`Self::follow_system_theme`] is `false`. + /// + /// Default: `Theme::Dark` (default to dark theme). + pub default_theme: Theme, } impl Default for NativeOptions { @@ -265,6 +282,49 @@ impl Default for NativeOptions { stencil_buffer: 0, hardware_acceleration: HardwareAcceleration::Preferred, renderer: Renderer::default(), + follow_system_theme: cfg!(target_os = "macos") || cfg!(target_os = "windows"), + default_theme: Theme::Dark, + } + } +} + +impl NativeOptions { + /// The theme used by the system. + #[cfg(feature = "dark-light")] + pub fn system_theme(&self) -> Option { + if self.follow_system_theme { + crate::profile_scope!("dark_light::detect"); + match dark_light::detect() { + dark_light::Mode::Dark => Some(Theme::Dark), + dark_light::Mode::Light => Some(Theme::Light), + } + } else { + None + } + } + + /// The theme used by the system. + #[cfg(not(feature = "dark-light"))] + pub fn system_theme(&self) -> Option { + None + } +} + +/// Dark or Light theme. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub enum Theme { + /// Dark mode: light text on a dark background. + Dark, + /// Light mode: dark text on a light background. + Light, +} + +impl Theme { + pub(crate) fn egui_visuals(self) -> egui::Visuals { + match self { + Self::Dark => egui::Visuals::dark(), + Self::Light => egui::Visuals::light(), } } } @@ -531,9 +591,10 @@ pub struct IntegrationInfo { /// If the app is running in a Web context, this returns information about the environment. pub web_info: Option, - /// Does the system prefer dark mode (over light mode)? + /// Does the OS use dark or light mode? + /// /// `None` means "don't know". - pub prefer_dark_mode: Option, + pub system_theme: Option, /// Seconds of cpu usage (in seconds) of UI code on the previous frame. /// `None` if this is the first frame. diff --git a/eframe/src/native/epi_integration.rs b/eframe/src/native/epi_integration.rs index 0365d1683c5..597e0c85595 100644 --- a/eframe/src/native/epi_integration.rs +++ b/eframe/src/native/epi_integration.rs @@ -1,4 +1,4 @@ -use crate::{epi, WindowInfo}; +use crate::{epi, Theme, WindowInfo}; use egui_winit::{native_pixels_per_point, WindowSettings}; use winit::event_loop::EventLoopWindowTarget; @@ -47,12 +47,7 @@ pub fn window_builder( max_window_size, resizable, transparent, - vsync: _, // used in `fn create_display` - multisampling: _, // used in `fn create_display` - depth_buffer: _, // used in `fn create_display` - stencil_buffer: _, // used in `fn create_display` - hardware_acceleration: _, // used in `fn create_display` - renderer: _, // used in `fn run_native` + .. } = native_options; let window_icon = icon_data.clone().and_then(load_icon); @@ -187,6 +182,7 @@ impl EpiIntegration { event_loop: &EventLoopWindowTarget, max_texture_side: usize, window: &winit::window::Window, + system_theme: Option, storage: Option>, #[cfg(feature = "glow")] gl: Option>, #[cfg(feature = "wgpu")] render_state: Option, @@ -195,12 +191,10 @@ impl EpiIntegration { *egui_ctx.memory() = load_egui_memory(storage.as_deref()).unwrap_or_default(); - let prefer_dark_mode = prefer_dark_mode(); - let frame = epi::Frame { info: epi::IntegrationInfo { web_info: None, - prefer_dark_mode, + system_theme, cpu_usage: None, native_pixels_per_point: Some(native_pixels_per_point(window)), window_info: read_window_info(window, egui_ctx.pixels_per_point()), @@ -213,12 +207,6 @@ impl EpiIntegration { render_state, }; - if prefer_dark_mode == Some(true) { - egui_ctx.set_visuals(egui::Visuals::dark()); - } else { - egui_ctx.set_visuals(egui::Visuals::light()); - } - let mut egui_winit = egui_winit::State::new(event_loop); egui_winit.set_max_texture_side(max_texture_side); let pixels_per_point = window.scale_factor() as f32; @@ -376,16 +364,3 @@ pub fn load_egui_memory(_storage: Option<&dyn epi::Storage>) -> Option Option { - match dark_light::detect() { - dark_light::Mode::Dark => Some(true), - dark_light::Mode::Light => Some(false), - } -} - -#[cfg(not(feature = "dark-light"))] -fn prefer_dark_mode() -> Option { - None -} diff --git a/eframe/src/native/run.rs b/eframe/src/native/run.rs index 5fb69425bee..525ed928cb9 100644 --- a/eframe/src/native/run.rs +++ b/eframe/src/native/run.rs @@ -66,15 +66,22 @@ pub fn run_glow( let mut painter = egui_glow::Painter::new(gl.clone(), None, "") .unwrap_or_else(|error| panic!("some OpenGL error occurred {}\n", error)); + let system_theme = native_options.system_theme(); let mut integration = epi_integration::EpiIntegration::new( &event_loop, painter.max_texture_side(), gl_window.window(), + system_theme, storage, Some(gl.clone()), #[cfg(feature = "wgpu")] None, ); + integration.egui_ctx.set_visuals( + system_theme + .unwrap_or(native_options.default_theme) + .egui_visuals(), + ); { let event_loop_proxy = egui::mutex::Mutex::new(event_loop.create_proxy()); @@ -248,15 +255,22 @@ pub fn run_wgpu( let render_state = painter.get_render_state().expect("Uninitialized"); + let system_theme = native_options.system_theme(); let mut integration = epi_integration::EpiIntegration::new( &event_loop, painter.max_texture_side().unwrap_or(2048), &window, + system_theme, storage, #[cfg(feature = "glow")] None, Some(render_state.clone()), ); + integration.egui_ctx.set_visuals( + system_theme + .unwrap_or(native_options.default_theme) + .egui_visuals(), + ); { let event_loop_proxy = egui::mutex::Mutex::new(event_loop.create_proxy()); diff --git a/egui_demo_app/src/wrap_app.rs b/egui_demo_app/src/wrap_app.rs index ff43549b56f..fd32bb3ed2e 100644 --- a/egui_demo_app/src/wrap_app.rs +++ b/egui_demo_app/src/wrap_app.rs @@ -112,12 +112,6 @@ impl WrapApp { } } - if cc.integration_info.prefer_dark_mode == Some(false) { - cc.egui_ctx.set_visuals(egui::Visuals::light()); // use light mode if explicitly asked for - } else { - cc.egui_ctx.set_visuals(egui::Visuals::dark()); // use dark mode if there is no preference, or the preference is dark mode - } - slf }