egui/memory/
theme.rs

1use crate::Button;
2
3/// Dark or Light theme.
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
6pub enum Theme {
7    /// Dark mode: light text on a dark background.
8    Dark,
9
10    /// Light mode: dark text on a light background.
11    Light,
12}
13
14impl Theme {
15    /// Default visuals for this theme.
16    pub fn default_visuals(self) -> crate::Visuals {
17        match self {
18            Self::Dark => crate::Visuals::dark(),
19            Self::Light => crate::Visuals::light(),
20        }
21    }
22
23    /// Default style for this theme.
24    pub fn default_style(self) -> crate::Style {
25        crate::Style {
26            visuals: self.default_visuals(),
27            ..Default::default()
28        }
29    }
30
31    /// Chooses between [`Self::Dark`] or [`Self::Light`] based on a boolean value.
32    pub fn from_dark_mode(dark_mode: bool) -> Self {
33        if dark_mode { Self::Dark } else { Self::Light }
34    }
35}
36
37impl Theme {
38    /// Show small toggle-button for light and dark mode.
39    /// This is not the best design as it doesn't allow switching back to "follow system".
40    #[must_use]
41    pub(crate) fn small_toggle_button(self, ui: &mut crate::Ui) -> Option<Self> {
42        #![allow(clippy::collapsible_else_if)]
43        if self == Self::Dark {
44            if ui
45                .add(Button::new("☀").frame(false))
46                .on_hover_text("Switch to light mode")
47                .clicked()
48            {
49                return Some(Self::Light);
50            }
51        } else {
52            if ui
53                .add(Button::new("🌙").frame(false))
54                .on_hover_text("Switch to dark mode")
55                .clicked()
56            {
57                return Some(Self::Dark);
58            }
59        }
60        None
61    }
62}
63
64/// The user's theme preference.
65#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
66#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
67pub enum ThemePreference {
68    /// Dark mode: light text on a dark background.
69    Dark,
70
71    /// Light mode: dark text on a light background.
72    Light,
73
74    /// Follow the system's theme preference.
75    #[default]
76    System,
77}
78
79impl From<Theme> for ThemePreference {
80    fn from(value: Theme) -> Self {
81        match value {
82            Theme::Dark => Self::Dark,
83            Theme::Light => Self::Light,
84        }
85    }
86}
87
88impl ThemePreference {
89    /// Show radio-buttons to switch between light mode, dark mode and following the system theme.
90    pub fn radio_buttons(&mut self, ui: &mut crate::Ui) {
91        ui.horizontal(|ui| {
92            let system_theme = ui.ctx().input(|i| i.raw.system_theme);
93
94            ui.selectable_value(self, Self::System, "💻 System")
95                .on_hover_ui(|ui| {
96                    ui.label("Follow the system theme preference.");
97
98                    ui.add_space(4.0);
99
100                    if let Some(system_theme) = system_theme {
101                        ui.label(format!(
102                            "The current system theme is: {}",
103                            match system_theme {
104                                Theme::Dark => "dark",
105                                Theme::Light => "light",
106                            }
107                        ));
108                    } else {
109                        ui.label("The system theme is unknown.");
110                    }
111                });
112
113            ui.selectable_value(self, Self::Dark, "🌙 Dark")
114                .on_hover_text("Use the dark mode theme");
115
116            ui.selectable_value(self, Self::Light, "☀ Light")
117                .on_hover_text("Use the light mode theme");
118        });
119    }
120}