Skip to content

Commit

Permalink
Add separate light and dark styles
Browse files Browse the repository at this point in the history
  • Loading branch information
bash committed Sep 3, 2024
1 parent 7d8fe1e commit 0faab82
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 78 deletions.
2 changes: 1 addition & 1 deletion crates/eframe/src/epi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub struct CreationContext<'s> {
/// The egui Context.
///
/// You can use this to customize the look of egui, e.g to call [`egui::Context::set_fonts`],
/// [`egui::Context::set_visuals`] etc.
/// [`egui::Context::set_dark_visuals`] etc.
pub egui_ctx: egui::Context,

/// Information about the surrounding environment.
Expand Down
100 changes: 85 additions & 15 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1641,7 +1641,7 @@ impl Context {
self.options(|opt| opt.style().clone())
}

/// Mutate the [`Style`] used by all subsequent windows, panels etc.
/// Mutate the [`Style`]s used by all subsequent windows, panels etc. in both dark and light mode.
///
/// Example:
/// ```
Expand All @@ -1650,30 +1650,93 @@ impl Context {
/// style.spacing.item_spacing = egui::vec2(10.0, 20.0);
/// });
/// ```
pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
self.options_mut(|opt| mutate_style(std::sync::Arc::make_mut(&mut opt.style)));
pub fn style_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {
self.options_mut(|opt| {
mutate_style(std::sync::Arc::make_mut(&mut opt.dark_style));
mutate_style(std::sync::Arc::make_mut(&mut opt.light_style));
});
}

/// The [`Style`] used by all subsequent windows, panels etc in dark mode.
pub fn dark_style(&self) -> Arc<Style> {
self.options(|opt| opt.dark_style.clone())
}

/// Mutate the [`Style`] used by all subsequent windows, panels etc in dark mode.
///
/// Example:
/// ```
/// # let mut ctx = egui::Context::default();
/// ctx.dark_style_mut(|style| {
/// style.spacing.item_spacing = egui::vec2(10.0, 20.0);
/// });
/// ```
pub fn dark_style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
self.options_mut(|opt| mutate_style(std::sync::Arc::make_mut(&mut opt.dark_style)));
}

/// The [`Style`] used by all new windows, panels etc. in dark mode.
/// Use [`Self::set_theme`] to choose between dark and light mode.
///
/// You can also change this using [`Self::dark_style_mut`]
///
/// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
pub fn set_dark_style(&self, style: impl Into<std::sync::Arc<crate::Style>>) {
self.options_mut(|opt| opt.dark_style = style.into());
}

/// The [`crate::Visuals`] used by all subsequent windows, panels etc. in dark mode.
///
/// You can also use [`Ui::visuals_mut`] to change the visuals of a single [`Ui`].
///
/// Example:
/// ```
/// # let mut ctx = egui::Context::default();
/// ctx.set_dark_visuals(egui::Visuals { panel_fill: egui::Color32::RED, ..Default::default() });
/// ```
pub fn set_dark_visuals(&self, visuals: crate::Visuals) {
self.dark_style_mut(|style| style.visuals = visuals);
}

/// The [`Style`] used by all subsequent windows, panels etc in light mode.
pub fn light_style(&self) -> Arc<Style> {
self.options(|opt| opt.light_style.clone())
}

/// Mutate the [`Style`] used by all subsequent windows, panels etc in light mode.
///
/// Example:
/// ```
/// # let mut ctx = egui::Context::default();
/// ctx.light_style_mut(|style| {
/// style.spacing.item_spacing = egui::vec2(10.0, 20.0);
/// });
/// ```
pub fn light_style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
self.options_mut(|opt| mutate_style(std::sync::Arc::make_mut(&mut opt.light_style)));
}

/// The [`Style`] used by all new windows, panels etc.
/// The [`Style`] used by all new windows, panels etc. in light mode.
/// Use [`Self::set_theme`] to choose between dark and light mode.
///
/// You can also change this using [`Self::style_mut`]
/// You can also change this using [`Self::dark_style_mut`]
///
/// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
pub fn set_style(&self, style: impl Into<Arc<Style>>) {
self.options_mut(|opt| opt.style = style.into());
pub fn set_light_style(&self, style: impl Into<std::sync::Arc<crate::Style>>) {
self.options_mut(|opt| opt.light_style = style.into());
}

/// The [`crate::Visuals`] used by all subsequent windows, panels etc.
/// The [`crate::Visuals`] used by all subsequent windows, panels etc. in light mode.
///
/// You can also use [`Ui::visuals_mut`] to change the visuals of a single [`Ui`].
///
/// Example:
/// ```
/// # let mut ctx = egui::Context::default();
/// ctx.set_visuals(egui::Visuals::light()); // Switch to light mode
/// ctx.set_light_visuals(egui::Visuals { panel_fill: egui::Color32::RED, ..Default::default() });
/// ```
pub fn set_visuals(&self, visuals: crate::Visuals) {
self.options_mut(|opt| std::sync::Arc::make_mut(&mut opt.style).visuals = visuals);
pub fn set_light_visuals(&self, visuals: crate::Visuals) {
self.light_style_mut(|style| style.visuals = visuals);
}

/// The number of physical pixels for each logical point.
Expand Down Expand Up @@ -2895,11 +2958,18 @@ impl Context {
}

impl Context {
/// Edit the active [`Style`].
pub fn style_ui(&self, ui: &mut Ui) {
let mut style: Style = (*self.style()).clone();
/// Edit the [`Style`] used in dark mode.
pub fn dark_style_ui(&self, ui: &mut Ui) {
let mut style: Style = (*self.dark_style()).clone();
style.ui(ui);
self.set_dark_style(style);
}

/// Edit the [`Style`] used in light mode.
pub fn light_style_ui(&self, ui: &mut Ui) {
let mut style: Style = (*self.light_style()).clone();
style.ui(ui);
self.set_style(style);
self.set_light_style(style);
}
}

Expand Down
46 changes: 23 additions & 23 deletions crates/egui/src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,12 @@ impl FocusDirection {
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct Options {
/// The default style for new [`Ui`](crate::Ui):s.
/// The default style for new [`Ui`](crate::Ui):s in dark mode.
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) style: std::sync::Arc<Style>,
pub(crate) dark_style: std::sync::Arc<Style>,

/// The default style for new [`Ui`](crate::Ui):s in light mode.
pub(crate) light_style: std::sync::Arc<Style>,

/// Whether to update the visuals according to the system theme or not.
///
Expand Down Expand Up @@ -279,7 +282,8 @@ impl Default for Options {
};

Self {
style: Default::default(),
dark_style: std::sync::Arc::new(Theme::Dark.default_style()),
light_style: std::sync::Arc::new(Theme::Light.default_style()),
theme_preference: ThemePreference::System,
fallback_theme: Theme::Dark,
system_theme: None,
Expand All @@ -301,22 +305,7 @@ impl Default for Options {

impl Options {
pub(crate) fn begin_frame(&mut self, new_raw_input: &RawInput) {
if self.theme_preference == ThemePreference::System {
let theme_from_visuals = Theme::from_dark_mode(self.style.visuals.dark_mode);
let current_system_theme = self.system_theme.unwrap_or(theme_from_visuals);
let new_system_theme = new_raw_input.system_theme.unwrap_or(self.fallback_theme);

// Only update the visuals if the system theme has changed.
// This allows users to change the visuals without them
// getting reset on the next frame.
if current_system_theme != new_system_theme || self.system_theme.is_none() {
self.system_theme = Some(new_system_theme);
if theme_from_visuals != new_system_theme {
let visuals = new_system_theme.default_visuals();
std::sync::Arc::make_mut(&mut self.style).visuals = visuals;
}
}
}
self.system_theme = new_raw_input.system_theme;
}

/// The currently active theme (may depend on the system theme).
Expand All @@ -329,16 +318,20 @@ impl Options {
}

pub(crate) fn style(&self) -> &std::sync::Arc<Style> {
&self.style
match self.theme() {
Theme::Dark => &self.dark_style,
Theme::Light => &self.light_style,
}
}
}

impl Options {
/// Show the options in the ui.
pub fn ui(&mut self, ui: &mut crate::Ui) {
let Self {
style, // covered above
theme_preference: _,
dark_style, // covered above
light_style,
theme_preference,
fallback_theme: _,
system_theme: _,
zoom_factor: _, // TODO(emilk)
Expand Down Expand Up @@ -378,7 +371,14 @@ impl Options {
CollapsingHeader::new("🎑 Style")
.default_open(true)
.show(ui, |ui| {
std::sync::Arc::make_mut(style).ui(ui);
theme_preference.radio_buttons(ui);

CollapsingHeader::new("Dark")
.default_open(true)
.show(ui, |ui| std::sync::Arc::make_mut(dark_style).ui(ui));
CollapsingHeader::new("Light")
.default_open(true)
.show(ui, |ui| std::sync::Arc::make_mut(light_style).ui(ui));
});

CollapsingHeader::new("✒ Painting")
Expand Down
40 changes: 40 additions & 0 deletions crates/egui/src/memory/theme.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::Button;

/// Dark or Light theme.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand Down Expand Up @@ -36,6 +38,33 @@ impl Theme {
}
}

impl Theme {
/// Show small toggle-button for light and dark mode.
/// This is not the best design as it doesn't allow switching back to "follow system".
#[must_use]
pub(crate) fn small_toggle_button(self, ui: &mut crate::Ui) -> Option<Self> {
#![allow(clippy::collapsible_else_if)]
if self == Self::Dark {
if ui
.add(Button::new("☀").frame(false))
.on_hover_text("Switch to light mode")
.clicked()
{
return Some(Self::Light);
}
} else {
if ui
.add(Button::new("🌙").frame(false))
.on_hover_text("Switch to dark mode")
.clicked()
{
return Some(Self::Dark);
}
}
None
}
}

/// The user's theme preference.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand All @@ -58,3 +87,14 @@ impl From<Theme> for ThemePreference {
}
}
}

impl ThemePreference {
/// Show radio-buttons to switch between light mode, dark mode and following the system theme.
pub fn radio_buttons(&mut self, ui: &mut crate::Ui) {
ui.horizontal(|ui| {
ui.selectable_value(self, Self::Light, "☀ Light");
ui.selectable_value(self, Self::Dark, "🌙 Dark");
ui.selectable_value(self, Self::System, "System");
});
}
}
25 changes: 7 additions & 18 deletions crates/egui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ impl From<TextStyle> for FontSelection {
/// Specifies the look and feel of egui.
///
/// You can change the visuals of a [`Ui`] with [`Ui::style_mut`]
/// and of everything with [`crate::Context::set_style`].
/// and of everything with [`crate::Context::set_dark_style`] and [`crate::Context::set_light_style`].
/// To choose between dark and light style, use [`crate::Context::set_theme`].
///
/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.
#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -206,12 +207,10 @@ pub struct Style {
/// use egui::FontFamily::Proportional;
/// use egui::FontId;
/// use egui::TextStyle::*;
///
/// // Get current context style
/// let mut style = (*ctx.style()).clone();
/// use std::collections::BTreeMap;
///
/// // Redefine text_styles
/// style.text_styles = [
/// let text_styles: BTreeMap<_, _> = [
/// (Heading, FontId::new(30.0, Proportional)),
/// (Name("Heading2".into()), FontId::new(25.0, Proportional)),
/// (Name("Context".into()), FontId::new(23.0, Proportional)),
Expand All @@ -221,8 +220,8 @@ pub struct Style {
/// (Small, FontId::new(10.0, Proportional)),
/// ].into();
///
/// // Mutate global style with above changes
/// ctx.set_style(style);
/// // Mutate global style with new text styles
/// ctx.style_mut(move |style| style.text_styles = text_styles.clone());
/// ```
pub text_styles: BTreeMap<TextStyle, FontId>,

Expand Down Expand Up @@ -855,7 +854,7 @@ impl Default for TextCursorStyle {
/// Controls the visual style (colors etc) of egui.
///
/// You can change the visuals of a [`Ui`] with [`Ui::visuals_mut`]
/// and of everything with [`crate::Context::set_visuals`].
/// and of everything with [`crate::Context::set_dark_visuals`] and [`crate::Context::set_light_visuals`].
///
/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.
#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -1519,8 +1518,6 @@ impl Style {
scroll_animation,
} = self;

visuals.light_dark_radio_buttons(ui);

crate::Grid::new("_options").show(ui, |ui| {
ui.label("Override font id");
ui.vertical(|ui| {
Expand Down Expand Up @@ -1931,14 +1928,6 @@ impl WidgetVisuals {
}

impl Visuals {
/// Show radio-buttons to switch between light and dark mode.
pub fn light_dark_radio_buttons(&mut self, ui: &mut crate::Ui) {
ui.horizontal(|ui| {
ui.selectable_value(self, Self::light(), "☀ Light");
ui.selectable_value(self, Self::dark(), "🌙 Dark");
});
}

/// Show small toggle-button for light and dark mode.
#[must_use]
pub fn light_dark_small_toggle_button(&self, ui: &mut crate::Ui) -> Option<Self> {
Expand Down
6 changes: 3 additions & 3 deletions crates/egui/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ impl Ui {
/// Mutably borrow internal [`Style`].
/// Changes apply to this [`Ui`] and its subsequent children.
///
/// To set the style of all [`Ui`]:s, use [`Context::set_style`].
/// To set the style of all [`Ui`]:s, use [`Context::set_dark_style`] and [`Context::set_light_style`].
///
/// Example:
/// ```
Expand All @@ -338,7 +338,7 @@ impl Ui {

/// Changes apply to this [`Ui`] and its subsequent children.
///
/// To set the visuals of all [`Ui`]:s, use [`Context::set_visuals`].
/// To set the visuals of all [`Ui`]:s, use [`Context::set_dark_visuals`] and [`Context::set_light_visuals`].
pub fn set_style(&mut self, style: impl Into<Arc<Style>>) {
self.style = style.into();
}
Expand Down Expand Up @@ -378,7 +378,7 @@ impl Ui {
/// Mutably borrow internal `visuals`.
/// Changes apply to this [`Ui`] and its subsequent children.
///
/// To set the visuals of all [`Ui`]:s, use [`Context::set_visuals`].
/// To set the visuals of all [`Ui`]:s, use [`Context::set_dark_visuals`] and `Context::set_light_visuals`].
///
/// Example:
/// ```
Expand Down
Loading

0 comments on commit 0faab82

Please sign in to comment.