//! Use the built-in theme and styles. pub mod palette; pub use palette::Palette; use crate::Color; use std::borrow::Cow; use std::fmt; use std::sync::Arc; /// A built-in theme. #[derive(Debug, Clone, PartialEq)] pub enum Theme { /// The built-in light variant. Light, /// The built-in dark variant. Dark, /// The built-in Dracula variant. Dracula, /// The built-in Nord variant. Nord, /// The built-in Solarized Light variant. SolarizedLight, /// The built-in Solarized Dark variant. SolarizedDark, /// The built-in Gruvbox Light variant. GruvboxLight, /// The built-in Gruvbox Dark variant. GruvboxDark, /// The built-in Catppuccin Latte variant. CatppuccinLatte, /// The built-in Catppuccin Frappé variant. CatppuccinFrappe, /// The built-in Catppuccin Macchiato variant. CatppuccinMacchiato, /// The built-in Catppuccin Mocha variant. CatppuccinMocha, /// The built-in Tokyo Night variant. TokyoNight, /// The built-in Tokyo Night Storm variant. TokyoNightStorm, /// The built-in Tokyo Night Light variant. TokyoNightLight, /// The built-in Kanagawa Wave variant. KanagawaWave, /// The built-in Kanagawa Dragon variant. KanagawaDragon, /// The built-in Kanagawa Lotus variant. KanagawaLotus, /// The built-in Moonfly variant. Moonfly, /// The built-in Nightfly variant. Nightfly, /// The built-in Oxocarbon variant. Oxocarbon, /// The built-in Ferra variant: Ferra, /// A [`Theme`] that uses a [`Custom`] palette. Custom(Arc), } impl Theme { /// A list with all the defined themes. pub const ALL: &'static [Self] = &[ Self::Light, Self::Dark, Self::Dracula, Self::Nord, Self::SolarizedLight, Self::SolarizedDark, Self::GruvboxLight, Self::GruvboxDark, Self::CatppuccinLatte, Self::CatppuccinFrappe, Self::CatppuccinMacchiato, Self::CatppuccinMocha, Self::TokyoNight, Self::TokyoNightStorm, Self::TokyoNightLight, Self::KanagawaWave, Self::KanagawaDragon, Self::KanagawaLotus, Self::Moonfly, Self::Nightfly, Self::Oxocarbon, Self::Ferra, ]; /// Creates a new custom [`Theme`] from the given [`Palette`]. pub fn custom( name: impl Into>, palette: Palette, ) -> Self { Self::custom_with_fn(name, palette, palette::Extended::generate) } /// Creates a new custom [`Theme`] from the given [`Palette`], with /// a custom generator of a [`palette::Extended`]. pub fn custom_with_fn( name: impl Into>, palette: Palette, generate: impl FnOnce(Palette) -> palette::Extended, ) -> Self { Self::Custom(Arc::new(Custom::with_fn(name, palette, generate))) } /// Returns the [`Palette`] of the [`Theme`]. pub fn palette(&self) -> Palette { match self { Self::Light => Palette::LIGHT, Self::Dark => Palette::DARK, Self::Dracula => Palette::DRACULA, Self::Nord => Palette::NORD, Self::SolarizedLight => Palette::SOLARIZED_LIGHT, Self::SolarizedDark => Palette::SOLARIZED_DARK, Self::GruvboxLight => Palette::GRUVBOX_LIGHT, Self::GruvboxDark => Palette::GRUVBOX_DARK, Self::CatppuccinLatte => Palette::CATPPUCCIN_LATTE, Self::CatppuccinFrappe => Palette::CATPPUCCIN_FRAPPE, Self::CatppuccinMacchiato => Palette::CATPPUCCIN_MACCHIATO, Self::CatppuccinMocha => Palette::CATPPUCCIN_MOCHA, Self::TokyoNight => Palette::TOKYO_NIGHT, Self::TokyoNightStorm => Palette::TOKYO_NIGHT_STORM, Self::TokyoNightLight => Palette::TOKYO_NIGHT_LIGHT, Self::KanagawaWave => Palette::KANAGAWA_WAVE, Self::KanagawaDragon => Palette::KANAGAWA_DRAGON, Self::KanagawaLotus => Palette::KANAGAWA_LOTUS, Self::Moonfly => Palette::MOONFLY, Self::Nightfly => Palette::NIGHTFLY, Self::Oxocarbon => Palette::OXOCARBON, Self::Ferra => Palette::FERRA, Self::Custom(custom) => custom.palette, } } /// Returns the [`palette::Extended`] of the [`Theme`]. pub fn extended_palette(&self) -> &palette::Extended { match self { Self::Light => &palette::EXTENDED_LIGHT, Self::Dark => &palette::EXTENDED_DARK, Self::Dracula => &palette::EXTENDED_DRACULA, Self::Nord => &palette::EXTENDED_NORD, Self::SolarizedLight => &palette::EXTENDED_SOLARIZED_LIGHT, Self::SolarizedDark => &palette::EXTENDED_SOLARIZED_DARK, Self::GruvboxLight => &palette::EXTENDED_GRUVBOX_LIGHT, Self::GruvboxDark => &palette::EXTENDED_GRUVBOX_DARK, Self::CatppuccinLatte => &palette::EXTENDED_CATPPUCCIN_LATTE, Self::CatppuccinFrappe => &palette::EXTENDED_CATPPUCCIN_FRAPPE, Self::CatppuccinMacchiato => { &palette::EXTENDED_CATPPUCCIN_MACCHIATO } Self::CatppuccinMocha => &palette::EXTENDED_CATPPUCCIN_MOCHA, Self::TokyoNight => &palette::EXTENDED_TOKYO_NIGHT, Self::TokyoNightStorm => &palette::EXTENDED_TOKYO_NIGHT_STORM, Self::TokyoNightLight => &palette::EXTENDED_TOKYO_NIGHT_LIGHT, Self::KanagawaWave => &palette::EXTENDED_KANAGAWA_WAVE, Self::KanagawaDragon => &palette::EXTENDED_KANAGAWA_DRAGON, Self::KanagawaLotus => &palette::EXTENDED_KANAGAWA_LOTUS, Self::Moonfly => &palette::EXTENDED_MOONFLY, Self::Nightfly => &palette::EXTENDED_NIGHTFLY, Self::Oxocarbon => &palette::EXTENDED_OXOCARBON, Self::Ferra => &palette::EXTENDED_FERRA, Self::Custom(custom) => &custom.extended, } } } impl fmt::Display for Theme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.name()) } } /// A [`Theme`] with a customized [`Palette`]. #[derive(Debug, Clone, PartialEq)] pub struct Custom { name: Cow<'static, str>, palette: Palette, extended: palette::Extended, } impl Custom { /// Creates a [`Custom`] theme from the given [`Palette`]. pub fn new(name: String, palette: Palette) -> Self { Self::with_fn(name, palette, palette::Extended::generate) } /// Creates a [`Custom`] theme from the given [`Palette`] with /// a custom generator of a [`palette::Extended`]. pub fn with_fn( name: impl Into>, palette: Palette, generate: impl FnOnce(Palette) -> palette::Extended, ) -> Self { Self { name: name.into(), palette, extended: generate(palette), } } } impl fmt::Display for Custom { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name) } } /// A theme mode, denoting the tone or brightness of a theme. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Mode { /// No specific tone. #[default] None, /// A mode referring to themes with light tones. Light, /// A mode referring to themes with dark tones. Dark, } /// The base style of a theme. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { /// The background [`Color`] of the application. pub background_color: Color, /// The default text [`Color`] of the application. pub text_color: Color, } /// The default blank style of a theme. pub trait Base { /// Returns the default theme for the preferred [`Mode`]. fn default(preference: Mode) -> Self; /// Returns the [`Mode`] of the theme. fn mode(&self) -> Mode; /// Returns the default base [`Style`] of the theme. fn base(&self) -> Style; /// Returns the color [`Palette`] of the theme. /// /// This [`Palette`] may be used by the runtime for /// debugging purposes; like displaying performance /// metrics or devtools. fn palette(&self) -> Option; /// Returns the unique name of the theme. /// /// This name may be used to efficiently detect theme /// changes in some widgets. fn name(&self) -> &str; } impl Base for Theme { fn default(preference: Mode) -> Self { use std::env; use std::sync::OnceLock; static SYSTEM: OnceLock> = OnceLock::new(); let system = SYSTEM.get_or_init(|| { let name = env::var("ICED_THEME").ok()?; Theme::ALL .iter() .find(|theme| theme.to_string() == name) .cloned() }); if let Some(system) = system { return system.clone(); } match preference { Mode::None | Mode::Light => Self::Light, Mode::Dark => Self::Dark, } } fn mode(&self) -> Mode { if self.extended_palette().is_dark { Mode::Dark } else { Mode::Light } } fn base(&self) -> Style { default(self) } fn palette(&self) -> Option { Some(self.palette()) } fn name(&self) -> &str { match self { Self::Light => "Light", Self::Dark => "Dark", Self::Dracula => "Dracula", Self::Nord => "Nord", Self::SolarizedLight => "Solarized Light", Self::SolarizedDark => "Solarized Dark", Self::GruvboxLight => "Gruvbox Light", Self::GruvboxDark => "Gruvbox Dark", Self::CatppuccinLatte => "Catppuccin Latte", Self::CatppuccinFrappe => "Catppuccin Frappé", Self::CatppuccinMacchiato => "Catppuccin Macchiato", Self::CatppuccinMocha => "Catppuccin Mocha", Self::TokyoNight => "Tokyo Night", Self::TokyoNightStorm => "Tokyo Night Storm", Self::TokyoNightLight => "Tokyo Night Light", Self::KanagawaWave => "Kanagawa Wave", Self::KanagawaDragon => "Kanagawa Dragon", Self::KanagawaLotus => "Kanagawa Lotus", Self::Moonfly => "Moonfly", Self::Nightfly => "Nightfly", Self::Oxocarbon => "Oxocarbon", Self::Ferra => "Ferra", Self::Custom(custom) => &custom.name, } } } /// The default [`Style`] of a built-in [`Theme`]. pub fn default(theme: &Theme) -> Style { let palette = theme.extended_palette(); Style { background_color: palette.background.base.color, text_color: palette.background.base.text, } }