1use crate::Action;
9use crate::theme::ColorsSrgb;
10use std::collections::BTreeMap;
11use std::time::Duration;
12
13#[derive(Clone, Debug)]
15#[non_exhaustive]
16pub enum ThemeConfigMsg {
17    SetActiveTheme(String),
19    SetActiveScheme(String),
21    AddScheme(String, ColorsSrgb),
23    RemoveScheme(String),
25    FadeDurationMs(u32),
27}
28
29#[derive(Clone, Debug, PartialEq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct ThemeConfig {
33    #[cfg_attr(feature = "serde", serde(default))]
35    pub active_theme: String,
36
37    #[cfg_attr(feature = "serde", serde(default = "defaults::default_scheme"))]
39    pub active_scheme: String,
40
41    #[cfg_attr(feature = "serde", serde(default = "defaults::color_schemes"))]
45    pub color_schemes: BTreeMap<String, ColorsSrgb>,
46
47    #[cfg_attr(feature = "serde", serde(default = "defaults::cursor_blink_rate_ms"))]
49    pub cursor_blink_rate_ms: u32,
50
51    #[cfg_attr(feature = "serde", serde(default = "defaults::transition_fade_ms"))]
53    pub transition_fade_ms: u32,
54}
55
56impl Default for ThemeConfig {
57    fn default() -> Self {
58        ThemeConfig {
59            active_theme: "".to_string(),
60            active_scheme: defaults::default_scheme(),
61            color_schemes: defaults::color_schemes(),
62            cursor_blink_rate_ms: defaults::cursor_blink_rate_ms(),
63            transition_fade_ms: defaults::transition_fade_ms(),
64        }
65    }
66}
67
68impl ThemeConfig {
69    pub(super) fn change_config(&mut self, msg: ThemeConfigMsg) -> Action {
70        match msg {
71            ThemeConfigMsg::SetActiveTheme(theme) => self.set_active_theme(theme),
72            ThemeConfigMsg::SetActiveScheme(scheme) => self.set_active_scheme(scheme),
73            ThemeConfigMsg::AddScheme(scheme, colors) => self.add_scheme(scheme, colors),
74            ThemeConfigMsg::RemoveScheme(scheme) => self.remove_scheme(&scheme),
75            ThemeConfigMsg::FadeDurationMs(dur) => {
76                self.transition_fade_ms = dur;
77                Action::empty()
78            }
79        }
80    }
81}
82
83impl ThemeConfig {
84    pub fn set_active_theme(&mut self, theme: impl ToString) -> Action {
89        let theme = theme.to_string();
90        if self.active_theme == theme {
91            Action::empty()
92        } else {
93            self.active_theme = theme;
94            Action::THEME_SWITCH
95        }
96    }
97
98    #[inline]
102    pub fn active_scheme(&self) -> &str {
103        &self.active_scheme
104    }
105
106    pub fn set_active_scheme(&mut self, scheme: impl ToString) -> Action {
110        let scheme = scheme.to_string();
111        if self.color_schemes.keys().any(|k| *k == scheme) {
112            self.active_scheme = scheme.to_string();
113            Action::THEME_UPDATE
114        } else {
115            Action::empty()
116        }
117    }
118
119    #[inline]
121    pub fn color_schemes(&self) -> impl Iterator<Item = (&str, &ColorsSrgb)> {
122        self.color_schemes.iter().map(|(s, t)| (s.as_str(), t))
123    }
124
125    #[inline]
127    pub fn get_color_scheme(&self, name: &str) -> Option<&ColorsSrgb> {
128        self.color_schemes.get(name)
129    }
130
131    #[inline]
133    pub fn get_active_scheme(&self) -> &ColorsSrgb {
134        self.color_schemes
135            .get(&self.active_scheme)
136            .unwrap_or(&ColorsSrgb::LIGHT)
137    }
138
139    pub fn add_scheme(&mut self, scheme: impl ToString, colors: ColorsSrgb) -> Action {
141        self.color_schemes.insert(scheme.to_string(), colors);
142        Action::empty()
143    }
144
145    pub fn remove_scheme(&mut self, scheme: &str) -> Action {
147        self.color_schemes.remove(scheme);
148        if scheme == self.active_scheme {
149            Action::THEME_UPDATE
150        } else {
151            Action::empty()
152        }
153    }
154
155    #[inline]
157    pub fn cursor_blink_rate(&self) -> Duration {
158        Duration::from_millis(self.cursor_blink_rate_ms as u64)
159    }
160
161    #[inline]
163    pub fn transition_fade_duration(&self) -> Duration {
164        Duration::from_millis(self.transition_fade_ms as u64)
165    }
166}
167
168mod defaults {
169    use super::*;
170
171    #[cfg(not(feature = "dark-light"))]
172    pub fn default_scheme() -> String {
173        "light".to_string()
174    }
175
176    #[cfg(feature = "dark-light")]
177    pub fn default_scheme() -> String {
178        use dark_light::Mode;
179        match dark_light::detect() {
180            Ok(Mode::Dark) => "dark".to_string(),
181            _ => "light".to_string(),
182        }
183    }
184
185    pub fn color_schemes() -> BTreeMap<String, ColorsSrgb> {
186        let mut schemes = BTreeMap::new();
187        schemes.insert("light".to_string(), ColorsSrgb::LIGHT);
188        schemes.insert("dark".to_string(), ColorsSrgb::DARK);
189        schemes.insert("blue".to_string(), ColorsSrgb::BLUE);
190        schemes
191    }
192
193    pub fn cursor_blink_rate_ms() -> u32 {
194        600
195    }
196
197    pub fn transition_fade_ms() -> u32 {
198        150
199    }
200}