Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use dark-light on Mac and Windows #1726

Merged
merged 4 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions eframe/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C
* Add `NativeOptions::renderer` to switch between the rendering backends
* Fix clipboard on Wayland ([#1613](https://github.com/emilk/egui/pull/1613)).
* Allow running on native without hardware accelerated rendering. Change with `NativeOptions::hardware_acceleration` ([#1681]([#1693](https://github.com/emilk/egui/pull/1693)).
* `dark-light` (dark mode detection) is now enabled by default on Mac and Windows ([#1726](https://github.com/emilk/egui/pull/1726)).
* Add `NativeOptions::follow_system_theme` and `NativeOptions::default_theme` ([#1726](https://github.com/emilk/egui/pull/1726)).


## 0.18.0 - 2022-04-30
* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).
Expand Down
4 changes: 3 additions & 1 deletion eframe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
102 changes: 100 additions & 2 deletions eframe/src/epi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ pub trait App {
}

/// Selects the level of hardware graphics acceleration.
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum HardwareAcceleration {
/// Require graphics acceleration.
Expand All @@ -166,6 +167,7 @@ pub enum HardwareAcceleration {
/// Options controlling the behavior of a native window.
///
/// Only a single native window is currently supported.
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone)]
pub struct NativeOptions {
/// Sets whether or not the window will always be on top of other windows.
Expand Down Expand Up @@ -243,8 +245,25 @@ 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 <https://github.com/frewsxcv/rust-dark-light/issues/17>.
///
/// See also [`Self::default_theme`].
pub follow_system_theme: bool,

/// Which theme to use in case [`Self::follow_system_theme`] is `false`
/// or the `dark-light` feature is disabled.
///
/// Default: `Theme::Dark`.
pub default_theme: Theme,
}

#[cfg(not(target_arch = "wasm32"))]
impl Default for NativeOptions {
fn default() -> Self {
Self {
Expand All @@ -265,6 +284,84 @@ 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,
}
}
}

#[cfg(not(target_arch = "wasm32"))]
impl NativeOptions {
/// The theme used by the system.
#[cfg(feature = "dark-light")]
pub fn system_theme(&self) -> Option<Theme> {
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<Theme> {
None
}
}

// ----------------------------------------------------------------------------

/// Options when using `eframe` in a web page.
#[cfg(target_arch = "wasm32")]
pub struct WebOptions {
/// Try to detect and follow the system preferred setting for dark vs light mode.
///
/// See also [`Self::default_theme`].
///
/// Default: `true`.
pub follow_system_theme: bool,

/// Which theme to use in case [`Self::follow_system_theme`] is `false`
/// or system theme detection fails.
///
/// Default: `Theme::Dark`.
pub default_theme: Theme,
}

#[cfg(target_arch = "wasm32")]
impl Default for WebOptions {
fn default() -> Self {
Self {
follow_system_theme: true,
default_theme: Theme::Dark,
}
}
}

// ----------------------------------------------------------------------------

/// 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 {
/// Get the egui visuals corresponding to this theme.
///
/// Use with [`egui::Context::set_visuals`].
pub fn egui_visuals(self) -> egui::Visuals {
match self {
Self::Dark => egui::Visuals::dark(),
Self::Light => egui::Visuals::light(),
}
}
}
Expand Down Expand Up @@ -531,9 +628,10 @@ pub struct IntegrationInfo {
/// If the app is running in a Web context, this returns information about the environment.
pub web_info: Option<WebInfo>,

/// 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<bool>,
pub system_theme: Option<Theme>,

/// Seconds of cpu usage (in seconds) of UI code on the previous frame.
/// `None` if this is the first frame.
Expand Down
11 changes: 8 additions & 3 deletions eframe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,17 @@ pub use web_sys;
/// #[cfg(target_arch = "wasm32")]
/// #[wasm_bindgen]
/// pub fn start(canvas_id: &str) -> Result<(), eframe::wasm_bindgen::JsValue> {
/// eframe::start_web(canvas_id, Box::new(|cc| Box::new(MyEguiApp::new(cc))))
/// let web_options = eframe::WebOptions::default();
/// eframe::start_web(canvas_id, web_options, Box::new(|cc| Box::new(MyEguiApp::new(cc))))
/// }
/// ```
#[cfg(target_arch = "wasm32")]
pub fn start_web(canvas_id: &str, app_creator: AppCreator) -> Result<(), wasm_bindgen::JsValue> {
web::start(canvas_id, app_creator)?;
pub fn start_web(
canvas_id: &str,
web_options: WebOptions,
app_creator: AppCreator,
) -> Result<(), wasm_bindgen::JsValue> {
web::start(canvas_id, web_options, app_creator)?;
Ok(())
}

Expand Down
33 changes: 4 additions & 29 deletions eframe/src/native/epi_integration.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -187,6 +182,7 @@ impl EpiIntegration {
event_loop: &EventLoopWindowTarget<E>,
max_texture_side: usize,
window: &winit::window::Window,
system_theme: Option<Theme>,
storage: Option<Box<dyn epi::Storage>>,
#[cfg(feature = "glow")] gl: Option<std::sync::Arc<glow::Context>>,
#[cfg(feature = "wgpu")] render_state: Option<egui_wgpu::RenderState>,
Expand All @@ -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()),
Expand All @@ -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;
Expand Down Expand Up @@ -376,16 +364,3 @@ pub fn load_egui_memory(_storage: Option<&dyn epi::Storage>) -> Option<egui::Mem
#[cfg(not(feature = "persistence"))]
None
}

#[cfg(feature = "dark-light")]
fn prefer_dark_mode() -> Option<bool> {
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<bool> {
None
}
8 changes: 8 additions & 0 deletions eframe/src/native/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,19 @@ 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,
);
let theme = system_theme.unwrap_or(native_options.default_theme);
integration.egui_ctx.set_visuals(theme.egui_visuals());

{
let event_loop_proxy = egui::mutex::Mutex::new(event_loop.create_proxy());
Expand Down Expand Up @@ -248,15 +252,19 @@ 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()),
);
let theme = system_theme.unwrap_or(native_options.default_theme);
integration.egui_ctx.set_visuals(theme.egui_visuals());

{
let event_loop_proxy = egui::mutex::Mutex::new(event_loop.create_proxy());
Expand Down
30 changes: 20 additions & 10 deletions eframe/src/web/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,24 @@ pub struct AppRunner {
}

impl AppRunner {
pub fn new(canvas_id: &str, app_creator: epi::AppCreator) -> Result<Self, JsValue> {
pub fn new(
canvas_id: &str,
web_options: crate::WebOptions,
app_creator: epi::AppCreator,
) -> Result<Self, JsValue> {
let painter = WrappedGlowPainter::new(canvas_id).map_err(JsValue::from)?; // fail early

let prefer_dark_mode = super::prefer_dark_mode();
let system_theme = if web_options.follow_system_theme {
super::system_theme()
} else {
None
};

let info = epi::IntegrationInfo {
web_info: Some(epi::WebInfo {
location: web_location(),
}),
prefer_dark_mode,
system_theme,
cpu_usage: None,
native_pixels_per_point: Some(native_pixels_per_point()),
window_info: None,
Expand All @@ -158,11 +166,9 @@ impl AppRunner {

let egui_ctx = egui::Context::default();
load_memory(&egui_ctx);
if prefer_dark_mode == Some(true) {
egui_ctx.set_visuals(egui::Visuals::dark());
} else {
egui_ctx.set_visuals(egui::Visuals::light());
}

let theme = system_theme.unwrap_or(web_options.default_theme);
egui_ctx.set_visuals(theme.egui_visuals());

let app = app_creator(&epi::CreationContext {
egui_ctx: egui_ctx.clone(),
Expand Down Expand Up @@ -393,8 +399,12 @@ impl AppRunnerContainer {

/// Install event listeners to register different input events
/// and start running the given app.
pub fn start(canvas_id: &str, app_creator: epi::AppCreator) -> Result<AppRunnerRef, JsValue> {
let mut runner = AppRunner::new(canvas_id, app_creator)?;
pub fn start(
canvas_id: &str,
web_options: crate::WebOptions,
app_creator: epi::AppCreator,
) -> Result<AppRunnerRef, JsValue> {
let mut runner = AppRunner::new(canvas_id, web_options, app_creator)?;
runner.warm_up()?;
start_runner(runner)
}
Expand Down
15 changes: 8 additions & 7 deletions eframe/src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use web_sys::EventTarget;

use input::*;

use crate::Theme;

// ----------------------------------------------------------------------------

/// Current time in seconds (since undefined point in time)
Expand Down Expand Up @@ -55,13 +57,12 @@ pub fn native_pixels_per_point() -> f32 {
}
}

pub fn prefer_dark_mode() -> Option<bool> {
Some(
web_sys::window()?
.match_media("(prefers-color-scheme: dark)")
.ok()??
.matches(),
)
pub fn system_theme() -> Option<Theme> {
let dark_mode = web_sys::window()?
.match_media("(prefers-color-scheme: dark)")
.ok()??
.matches();
Some(if dark_mode { Theme::Dark } else { Theme::Light })
}

pub fn canvas_element(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
Expand Down
7 changes: 6 additions & 1 deletion egui_demo_app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,10 @@ pub fn start(canvas_id: &str) -> Result<(), wasm_bindgen::JsValue> {
// Redirect tracing to console.log and friends:
tracing_wasm::set_as_global_default();

eframe::start_web(canvas_id, Box::new(|cc| Box::new(WrapApp::new(cc))))
let web_options = eframe::WebOptions::default();
eframe::start_web(
canvas_id,
web_options,
Box::new(|cc| Box::new(WrapApp::new(cc))),
)
}
Loading