Skip to main content

iced_core/
theme.rs

1//! Use the built-in theme and styles.
2pub mod palette;
3
4pub use palette::Palette;
5
6use crate::Color;
7
8use std::borrow::Cow;
9use std::fmt;
10use std::sync::Arc;
11
12/// A built-in theme.
13#[derive(Debug, Clone, PartialEq)]
14pub enum Theme {
15    /// The built-in light variant.
16    Light,
17    /// The built-in dark variant.
18    Dark,
19    /// The built-in Dracula variant.
20    Dracula,
21    /// The built-in Nord variant.
22    Nord,
23    /// The built-in Solarized Light variant.
24    SolarizedLight,
25    /// The built-in Solarized Dark variant.
26    SolarizedDark,
27    /// The built-in Gruvbox Light variant.
28    GruvboxLight,
29    /// The built-in Gruvbox Dark variant.
30    GruvboxDark,
31    /// The built-in Catppuccin Latte variant.
32    CatppuccinLatte,
33    /// The built-in Catppuccin Frappé variant.
34    CatppuccinFrappe,
35    /// The built-in Catppuccin Macchiato variant.
36    CatppuccinMacchiato,
37    /// The built-in Catppuccin Mocha variant.
38    CatppuccinMocha,
39    /// The built-in Tokyo Night variant.
40    TokyoNight,
41    /// The built-in Tokyo Night Storm variant.
42    TokyoNightStorm,
43    /// The built-in Tokyo Night Light variant.
44    TokyoNightLight,
45    /// The built-in Kanagawa Wave variant.
46    KanagawaWave,
47    /// The built-in Kanagawa Dragon variant.
48    KanagawaDragon,
49    /// The built-in Kanagawa Lotus variant.
50    KanagawaLotus,
51    /// The built-in Moonfly variant.
52    Moonfly,
53    /// The built-in Nightfly variant.
54    Nightfly,
55    /// The built-in Oxocarbon variant.
56    Oxocarbon,
57    /// The built-in Ferra variant:
58    Ferra,
59    /// A [`Theme`] that uses a [`Custom`] palette.
60    Custom(Arc<Custom>),
61}
62
63impl Theme {
64    /// A list with all the defined themes.
65    pub const ALL: &'static [Self] = &[
66        Self::Light,
67        Self::Dark,
68        Self::Dracula,
69        Self::Nord,
70        Self::SolarizedLight,
71        Self::SolarizedDark,
72        Self::GruvboxLight,
73        Self::GruvboxDark,
74        Self::CatppuccinLatte,
75        Self::CatppuccinFrappe,
76        Self::CatppuccinMacchiato,
77        Self::CatppuccinMocha,
78        Self::TokyoNight,
79        Self::TokyoNightStorm,
80        Self::TokyoNightLight,
81        Self::KanagawaWave,
82        Self::KanagawaDragon,
83        Self::KanagawaLotus,
84        Self::Moonfly,
85        Self::Nightfly,
86        Self::Oxocarbon,
87        Self::Ferra,
88    ];
89
90    /// Creates a new custom [`Theme`] from the given [`Seed`](palette::Seed).
91    pub fn custom(name: impl Into<Cow<'static, str>>, seed: palette::Seed) -> Self {
92        Self::custom_with_fn(name, seed, Palette::generate)
93    }
94
95    /// Creates a new custom [`Theme`] from the given [`Seed`](palette::Seed), with
96    /// a custom generator of a [`Palette`].
97    pub fn custom_with_fn(
98        name: impl Into<Cow<'static, str>>,
99        palette: palette::Seed,
100        generate: impl FnOnce(palette::Seed) -> Palette,
101    ) -> Self {
102        Self::Custom(Arc::new(Custom::with_fn(name, palette, generate)))
103    }
104
105    /// Returns the [`Palette`] of the [`Theme`].
106    pub fn palette(&self) -> &palette::Palette {
107        match self {
108            Self::Light => &palette::LIGHT,
109            Self::Dark => &palette::DARK,
110            Self::Dracula => &palette::DRACULA,
111            Self::Nord => &palette::NORD,
112            Self::SolarizedLight => &palette::SOLARIZED_LIGHT,
113            Self::SolarizedDark => &palette::SOLARIZED_DARK,
114            Self::GruvboxLight => &palette::GRUVBOX_LIGHT,
115            Self::GruvboxDark => &palette::GRUVBOX_DARK,
116            Self::CatppuccinLatte => &palette::CATPPUCCIN_LATTE,
117            Self::CatppuccinFrappe => &palette::CATPPUCCIN_FRAPPE,
118            Self::CatppuccinMacchiato => &palette::CATPPUCCIN_MACCHIATO,
119            Self::CatppuccinMocha => &palette::CATPPUCCIN_MOCHA,
120            Self::TokyoNight => &palette::TOKYO_NIGHT,
121            Self::TokyoNightStorm => &palette::TOKYO_NIGHT_STORM,
122            Self::TokyoNightLight => &palette::TOKYO_NIGHT_LIGHT,
123            Self::KanagawaWave => &palette::KANAGAWA_WAVE,
124            Self::KanagawaDragon => &palette::KANAGAWA_DRAGON,
125            Self::KanagawaLotus => &palette::KANAGAWA_LOTUS,
126            Self::Moonfly => &palette::MOONFLY,
127            Self::Nightfly => &palette::NIGHTFLY,
128            Self::Oxocarbon => &palette::OXOCARBON,
129            Self::Ferra => &palette::FERRA,
130            Self::Custom(custom) => &custom.palette,
131        }
132    }
133
134    /// Returns the [`Seed`](palette::Seed) of the [`Theme`].
135    pub fn seed(&self) -> palette::Seed {
136        match self {
137            Self::Light => palette::Seed::LIGHT,
138            Self::Dark => palette::Seed::DARK,
139            Self::Dracula => palette::Seed::DRACULA,
140            Self::Nord => palette::Seed::NORD,
141            Self::SolarizedLight => palette::Seed::SOLARIZED_LIGHT,
142            Self::SolarizedDark => palette::Seed::SOLARIZED_DARK,
143            Self::GruvboxLight => palette::Seed::GRUVBOX_LIGHT,
144            Self::GruvboxDark => palette::Seed::GRUVBOX_DARK,
145            Self::CatppuccinLatte => palette::Seed::CATPPUCCIN_LATTE,
146            Self::CatppuccinFrappe => palette::Seed::CATPPUCCIN_FRAPPE,
147            Self::CatppuccinMacchiato => palette::Seed::CATPPUCCIN_MACCHIATO,
148            Self::CatppuccinMocha => palette::Seed::CATPPUCCIN_MOCHA,
149            Self::TokyoNight => palette::Seed::TOKYO_NIGHT,
150            Self::TokyoNightStorm => palette::Seed::TOKYO_NIGHT_STORM,
151            Self::TokyoNightLight => palette::Seed::TOKYO_NIGHT_LIGHT,
152            Self::KanagawaWave => palette::Seed::KANAGAWA_WAVE,
153            Self::KanagawaDragon => palette::Seed::KANAGAWA_DRAGON,
154            Self::KanagawaLotus => palette::Seed::KANAGAWA_LOTUS,
155            Self::Moonfly => palette::Seed::MOONFLY,
156            Self::Nightfly => palette::Seed::NIGHTFLY,
157            Self::Oxocarbon => palette::Seed::OXOCARBON,
158            Self::Ferra => palette::Seed::FERRA,
159            Self::Custom(custom) => custom.seed,
160        }
161    }
162}
163
164impl fmt::Display for Theme {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        f.write_str(self.name())
167    }
168}
169
170/// A [`Theme`] with a customized [`Palette`].
171#[derive(Debug, Clone, PartialEq)]
172pub struct Custom {
173    name: Cow<'static, str>,
174    seed: palette::Seed,
175    palette: Palette,
176}
177
178impl Custom {
179    /// Creates a [`Custom`] theme from the given [`Seed`](palette::Seed).
180    pub fn new(name: String, seed: palette::Seed) -> Self {
181        Self::with_fn(name, seed, Palette::generate)
182    }
183
184    /// Creates a [`Custom`] theme from the given [`Seed`](palette::Seed) with
185    /// a custom generator of a [`Palette`].
186    pub fn with_fn(
187        name: impl Into<Cow<'static, str>>,
188        seed: palette::Seed,
189        generate: impl FnOnce(palette::Seed) -> Palette,
190    ) -> Self {
191        Self {
192            name: name.into(),
193            seed,
194            palette: generate(seed),
195        }
196    }
197}
198
199impl fmt::Display for Custom {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        write!(f, "{}", self.name)
202    }
203}
204
205/// A theme mode, denoting the tone or brightness of a theme.
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
207pub enum Mode {
208    /// No specific tone.
209    #[default]
210    None,
211    /// A mode referring to themes with light tones.
212    Light,
213    /// A mode referring to themes with dark tones.
214    Dark,
215}
216
217/// The base style of a theme.
218#[derive(Debug, Clone, Copy, PartialEq)]
219pub struct Style {
220    /// The background [`Color`] of the application.
221    pub background_color: Color,
222
223    /// The default text [`Color`] of the application.
224    pub text_color: Color,
225}
226
227/// The default blank style of a theme.
228pub trait Base {
229    /// Returns the default theme for the preferred [`Mode`].
230    fn default(preference: Mode) -> Self;
231
232    /// Returns the [`Mode`] of the theme.
233    fn mode(&self) -> Mode;
234
235    /// Returns the default base [`Style`] of the theme.
236    fn base(&self) -> Style;
237
238    /// Returns the [`Seed`](palette::Seed) of the theme.
239    ///
240    /// This may be used by the runtime to recreate a [`Theme`] for
241    /// debugging purposes; like displaying performance metrics or devtools.
242    fn seed(&self) -> Option<palette::Seed>;
243
244    /// Returns the unique name of the theme.
245    ///
246    /// This name may be used to efficiently detect theme
247    /// changes in some widgets.
248    fn name(&self) -> &str;
249}
250
251impl Base for Theme {
252    fn default(preference: Mode) -> Self {
253        use std::env;
254        use std::sync::OnceLock;
255
256        static SYSTEM: OnceLock<Option<Theme>> = OnceLock::new();
257
258        let system = SYSTEM.get_or_init(|| {
259            let name = env::var("ICED_THEME").ok()?;
260
261            Theme::ALL
262                .iter()
263                .find(|theme| theme.to_string() == name)
264                .cloned()
265        });
266
267        if let Some(system) = system {
268            return system.clone();
269        }
270
271        match preference {
272            Mode::None | Mode::Light => Self::Light,
273            Mode::Dark => Self::Dark,
274        }
275    }
276
277    fn mode(&self) -> Mode {
278        if self.palette().is_dark {
279            Mode::Dark
280        } else {
281            Mode::Light
282        }
283    }
284
285    fn base(&self) -> Style {
286        default(self)
287    }
288
289    fn seed(&self) -> Option<palette::Seed> {
290        Some(self.seed())
291    }
292
293    fn name(&self) -> &str {
294        match self {
295            Self::Light => "Light",
296            Self::Dark => "Dark",
297            Self::Dracula => "Dracula",
298            Self::Nord => "Nord",
299            Self::SolarizedLight => "Solarized Light",
300            Self::SolarizedDark => "Solarized Dark",
301            Self::GruvboxLight => "Gruvbox Light",
302            Self::GruvboxDark => "Gruvbox Dark",
303            Self::CatppuccinLatte => "Catppuccin Latte",
304            Self::CatppuccinFrappe => "Catppuccin Frappé",
305            Self::CatppuccinMacchiato => "Catppuccin Macchiato",
306            Self::CatppuccinMocha => "Catppuccin Mocha",
307            Self::TokyoNight => "Tokyo Night",
308            Self::TokyoNightStorm => "Tokyo Night Storm",
309            Self::TokyoNightLight => "Tokyo Night Light",
310            Self::KanagawaWave => "Kanagawa Wave",
311            Self::KanagawaDragon => "Kanagawa Dragon",
312            Self::KanagawaLotus => "Kanagawa Lotus",
313            Self::Moonfly => "Moonfly",
314            Self::Nightfly => "Nightfly",
315            Self::Oxocarbon => "Oxocarbon",
316            Self::Ferra => "Ferra",
317            Self::Custom(custom) => &custom.name,
318        }
319    }
320}
321
322/// The default [`Style`] of a built-in [`Theme`].
323pub fn default(theme: &Theme) -> Style {
324    let palette = theme.palette();
325
326    Style {
327        background_color: palette.background.base.color,
328        text_color: palette.background.base.text,
329    }
330}