1use crate::config::Config;
7use crate::platform::CosmicTheme;
8use crate::render::Color;
9
10#[derive(Debug, Clone)]
12pub struct Theme {
13 pub background: Color,
15 pub card_background: Color,
17 pub card_border: Color,
19 pub text_primary: Color,
21 pub text_secondary: Color,
23 pub badge_background: Color,
25 pub badge_text: Color,
27 pub badge_matched_background: Color,
29 pub badge_matched_text: Color,
31 pub border_width: f32,
33 pub corner_radius: f32,
35}
36
37impl Theme {
38 pub fn from_cosmic() -> Option<Self> {
53 let cosmic = CosmicTheme::load()?;
54
55 let bg = cosmic.background.base.to_rgba();
58 let primary_base = cosmic.primary.base.to_rgba();
59 let primary_on = cosmic.primary.on.to_rgba();
60
61 let badge_base = cosmic.secondary.component.base.to_rgba();
65 let badge_on = cosmic.secondary.component.on.to_rgba();
66
67 let accent_base = cosmic.accent.base.to_rgba();
69 let accent_on = cosmic.accent.on.to_rgba();
70
71 let corner_radius = cosmic.corner_radii.radius_m[0];
73
74 tracing::info!(
75 "Loaded COSMIC {} theme",
76 if cosmic.is_dark { "dark" } else { "light" }
77 );
78
79 Some(Self {
80 background: Color::rgba(bg.0, bg.1, bg.2, 200),
82 card_background: Color::rgba(primary_base.0, primary_base.1, primary_base.2, 245),
84 card_border: Color::rgba(accent_base.0, accent_base.1, accent_base.2, 255),
86 text_primary: Color::rgba(primary_on.0, primary_on.1, primary_on.2, primary_on.3),
88 text_secondary: Color::rgba(
90 primary_on.0,
91 primary_on.1,
92 primary_on.2,
93 (primary_on.3 as f32 * 0.7) as u8,
94 ),
95 badge_background: Color::rgba(badge_base.0, badge_base.1, badge_base.2, 255),
97 badge_text: Color::rgba(badge_on.0, badge_on.1, badge_on.2, badge_on.3),
98 badge_matched_background: Color::rgba(accent_base.0, accent_base.1, accent_base.2, 255),
100 badge_matched_text: Color::rgba(accent_on.0, accent_on.1, accent_on.2, accent_on.3),
101 border_width: 2.0,
102 corner_radius,
103 })
104 }
105
106 pub fn from_config(config: &Config) -> Self {
111 if let Some(cosmic_theme) = Self::from_cosmic() {
113 return Self::apply_config_overrides(cosmic_theme, config);
115 }
116
117 Self::from_config_only(config)
119 }
120
121 fn apply_config_overrides(mut theme: Theme, config: &Config) -> Theme {
123 let settings = &config.settings;
124 let defaults = Config::default().settings;
125
126 if settings.background_color != defaults.background_color {
128 theme.background = Color::rgba(
129 settings.background_color.r,
130 settings.background_color.g,
131 settings.background_color.b,
132 settings.background_color.a,
133 );
134 }
135
136 if settings.card_color != defaults.card_color {
137 theme.card_background = Color::rgba(
138 settings.card_color.r,
139 settings.card_color.g,
140 settings.card_color.b,
141 settings.card_color.a,
142 );
143 }
144
145 if settings.border_color != defaults.border_color {
146 theme.card_border = Color::rgba(
147 settings.border_color.r,
148 settings.border_color.g,
149 settings.border_color.b,
150 settings.border_color.a,
151 );
152 }
153
154 if settings.text_color != defaults.text_color {
155 theme.text_primary = Color::rgba(
156 settings.text_color.r,
157 settings.text_color.g,
158 settings.text_color.b,
159 settings.text_color.a,
160 );
161 theme.text_secondary = Color::rgba(
162 settings.text_color.r,
163 settings.text_color.g,
164 settings.text_color.b,
165 (settings.text_color.a as f32 * 0.7) as u8,
166 );
167 }
168
169 if settings.hint_color != defaults.hint_color {
170 theme.badge_background = Color::rgba(
171 settings.hint_color.r,
172 settings.hint_color.g,
173 settings.hint_color.b,
174 settings.hint_color.a,
175 );
176 }
177
178 if settings.hint_matched_color != defaults.hint_matched_color {
179 theme.badge_matched_background = Color::rgba(
180 settings.hint_matched_color.r,
181 settings.hint_matched_color.g,
182 settings.hint_matched_color.b,
183 settings.hint_matched_color.a,
184 );
185 }
186
187 if settings.border_width != defaults.border_width {
188 theme.border_width = settings.border_width;
189 }
190
191 theme
192 }
193
194 fn from_config_only(config: &Config) -> Self {
196 let settings = &config.settings;
197
198 Self {
199 background: Color::rgba(
200 settings.background_color.r,
201 settings.background_color.g,
202 settings.background_color.b,
203 settings.background_color.a,
204 ),
205 card_background: Color::rgba(
206 settings.card_color.r,
207 settings.card_color.g,
208 settings.card_color.b,
209 settings.card_color.a,
210 ),
211 card_border: Color::rgba(
212 settings.border_color.r,
213 settings.border_color.g,
214 settings.border_color.b,
215 settings.border_color.a,
216 ),
217 text_primary: Color::rgba(
218 settings.text_color.r,
219 settings.text_color.g,
220 settings.text_color.b,
221 settings.text_color.a,
222 ),
223 text_secondary: Color::rgba(
224 settings.text_color.r,
225 settings.text_color.g,
226 settings.text_color.b,
227 (settings.text_color.a as f32 * 0.7) as u8,
228 ),
229 badge_background: Color::rgba(
230 settings.hint_color.r,
231 settings.hint_color.g,
232 settings.hint_color.b,
233 settings.hint_color.a,
234 ),
235 badge_text: Color::rgb(255, 255, 255),
236 badge_matched_background: Color::rgba(
237 settings.hint_matched_color.r,
238 settings.hint_matched_color.g,
239 settings.hint_matched_color.b,
240 settings.hint_matched_color.a,
241 ),
242 badge_matched_text: Color::rgb(255, 255, 255),
243 border_width: settings.border_width,
244 corner_radius: 8.0,
245 }
246 }
247}
248
249impl Default for Theme {
250 fn default() -> Self {
251 if let Some(cosmic_theme) = Self::from_cosmic() {
253 return cosmic_theme;
254 }
255
256 Self {
258 background: Color::rgba(0, 0, 0, 200),
259 card_background: Color::rgba(30, 30, 30, 240),
260 card_border: Color::rgba(80, 80, 80, 255),
261 text_primary: Color::rgb(255, 255, 255),
262 text_secondary: Color::rgba(255, 255, 255, 180),
263 badge_background: Color::rgba(100, 100, 100, 255),
264 badge_text: Color::rgb(255, 255, 255),
265 badge_matched_background: Color::rgba(76, 175, 80, 255),
266 badge_matched_text: Color::rgb(255, 255, 255),
267 border_width: 2.0,
268 corner_radius: 16.0,
269 }
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_default_theme() {
279 let theme = Theme::default();
280 assert!(theme.border_width > 0.0);
281 assert!(theme.corner_radius > 0.0);
282 }
283
284 #[test]
285 fn test_cosmic_theme_loading() {
286 let theme = Theme::from_cosmic();
288 if let Some(t) = theme {
289 println!("Loaded COSMIC theme with corner_radius={}", t.corner_radius);
290 } else {
291 println!("COSMIC theme not available (expected on non-COSMIC systems)");
292 }
293 }
294}