1use handlebars::Handlebars;
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
7pub enum AccentsColor {
8 Blue,
9 Teal,
10 Green,
11 Yellow,
12 Orange,
13 Red,
14 Pink,
15 Purple,
16 Slate,
17}
18
19#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
20pub enum Mode {
21 Light,
22 Dark,
23}
24
25#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
26pub enum GtkVersion {
27 Gtk3,
28 Gtk4,
29}
30
31fn default_version() -> String {
32 "0.0.1".to_string()
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Preset {
37 pub name: String,
38
39 #[serde(default = "default_version")]
40 pub version: String,
41
42 #[serde(default)]
43 pub author: Author,
44 #[serde(default)]
45 pub description: String,
46 #[serde(default)]
47 pub supported: Supported,
48 #[serde(default)]
49 pub license: License,
50 pub variables: Variables,
51 pub palette: Palette,
52 #[serde(alias = "custom_css")]
53 pub custom: Custom,
54 #[serde(default)]
55 pub shell: Shell,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct Shell {
60 #[serde(default = "Shell::default_bg_color")]
61 pub bg_color: Variable,
62 #[serde(default = "Shell::default_fg_color")]
63 pub fg_color: Variable,
64 #[serde(default = "Shell::default_system_bg_color")]
65 pub system_bg_color: Variable,
66 #[serde(default = "Shell::default_selected_bg_color")]
67 pub selected_bg_color: Variable,
68 #[serde(default = "Shell::default_selected_fg_color")]
69 pub selected_fg_color: Variable,
70 #[serde(default = "Shell::default_panel_bg_color")]
71 pub panel_bg_color: Variable,
72 #[serde(default = "Shell::default_osd_bg_color")]
73 pub osd_bg_color: Variable,
74 #[serde(default = "Shell::default_osd_fg_color")]
75 pub osd_fg_color: Variable,
76 #[serde(default = "Shell::default_system_fg_color")]
77 pub system_fg_color: Variable,
78 #[serde(default = "Shell::default_panel_fg_color")]
79 pub panel_fg_color: Variable,
80}
81
82impl Default for Shell {
83 fn default() -> Self {
84 Shell {
85 bg_color: Shell::default_bg_color(),
86 fg_color: Shell::default_fg_color(),
87 system_bg_color: Shell::default_system_bg_color(),
88 system_fg_color: Shell::default_system_fg_color(),
89 selected_bg_color: Shell::default_selected_bg_color(),
90 selected_fg_color: Shell::default_selected_fg_color(),
91 panel_bg_color: Shell::default_panel_bg_color(),
92 panel_fg_color: Shell::default_panel_fg_color(),
93 osd_bg_color: Shell::default_osd_bg_color(),
94 osd_fg_color: Shell::default_osd_fg_color(),
95 }
96 }
97}
98
99impl Shell {
100 fn default_bg_color() -> Variable {
101 Variable::Mode {
102 light: "#ffffff".to_string(),
103 dark: "#1e1e1e".to_string(),
104 }
105 }
106
107 fn default_panel_fg_color() -> Variable {
108 Variable::Mode {
109 light: "rgba(0, 0, 0, 0.8)".to_string(),
110 dark: "#ffffff".to_string(),
111 }
112 }
113
114 fn default_system_fg_color() -> Variable {
115 Variable::Mode {
116 light: "rgba(0, 0, 0, 0.8)".to_string(),
117 dark: "#ffffff".to_string(),
118 }
119 }
120
121 fn default_fg_color() -> Variable {
122 Variable::Mode {
123 light: "rgba(0, 0, 0, 0.8)".to_string(),
124 dark: "#ffffff".to_string(),
125 }
126 }
127
128 fn default_system_bg_color() -> Variable {
129 Variable::Mode {
130 light: "#fafafa".to_string(),
131 dark: "#242424".to_string(),
132 }
133 }
134
135 fn default_selected_bg_color() -> Variable {
136 Variable::Mode {
137 light: "#3584e4".to_string(),
138 dark: "#78aeed".to_string(),
139 }
140 }
141
142 fn default_selected_fg_color() -> Variable {
143 Variable::Mode {
144 light: "#ffffff".to_string(),
145 dark: "#ffffff".to_string(),
146 }
147 }
148
149 fn default_panel_bg_color() -> Variable {
150 Variable::Mode {
151 light: "#f2f2f2".to_string(),
152 dark: "#2a2a2a".to_string(),
153 }
154 }
155
156 fn default_osd_bg_color() -> Variable {
157 Variable::Mode {
158 light: "#f2f2f2".to_string(),
159 dark: "#2a2a2a".to_string(),
160 }
161 }
162
163 fn default_osd_fg_color() -> Variable {
164 Variable::Mode {
165 light: "rgba(0, 0, 0, 0.8)".to_string(),
166 dark: "#ffffff".to_string(),
167 }
168 }
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct Author {
173 name: String,
174 #[serde(default)]
175 email: String,
176 #[serde(default)]
177 url: String,
178}
179
180impl Default for Author {
181 fn default() -> Self {
182 Author {
183 name: "Anonymous".to_string(),
184 email: "".to_string(),
185 url: "".to_string(),
186 }
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct Supported {
192 gnome: String,
193 adw: String,
194 gtk: String,
195 mode: Vec<String>,
196 accents: Vec<String>,
197}
198
199impl Default for Supported {
200 fn default() -> Self {
201 Supported {
202 gnome: ">46".to_string(),
203 adw: ">1.5".to_string(),
204 gtk: ">3.24".to_string(),
205 mode: vec!["light".to_string(), "dark".to_string()],
206 accents: vec![
207 "blue".to_string(),
208 "green".to_string(),
209 "red".to_string(),
210 "yellow".to_string(),
211 "purple".to_string(),
212 "pink".to_string(),
213 "orange".to_string(),
214 "slate".to_string(),
215 "teal".to_string(),
216 ],
217 }
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
222pub enum License {
223 GPL3,
224 MIT,
225 Apache2,
226 BSD,
227 LGPL3,
228 AGPL3,
229 MPL2,
230 CC0,
231 CCBY,
232 CCBYSA,
233 CCBYNC,
234 CCBYNCSA,
235 CCBYND,
236 CCBYNCND,
237}
238
239impl Default for License {
240 fn default() -> Self {
241 License::GPL3
242 }
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct Variables {
247 #[serde(default = "Variables::default_accent_color")]
248 pub accent_color: Variable,
249 #[serde(default = "Variables::default_accent_bg_color")]
250 pub accent_bg_color: Variable,
251 #[serde(default = "Variables::default_accent_fg_color")]
252 pub accent_fg_color: Variable,
253 #[serde(default = "Variables::default_destructive_color")]
254 pub destructive_color: Variable,
255 #[serde(default = "Variables::default_destructive_bg_color")]
256 pub destructive_bg_color: Variable,
257 #[serde(default = "Variables::default_destructive_fg_color")]
258 pub destructive_fg_color: Variable,
259 #[serde(default = "Variables::default_success_color")]
260 pub success_color: Variable,
261 #[serde(default = "Variables::default_success_bg_color")]
262 pub success_bg_color: Variable,
263 #[serde(default = "Variables::default_success_fg_color")]
264 pub success_fg_color: Variable,
265 #[serde(default = "Variables::default_warning_color")]
266 pub warning_color: Variable,
267 #[serde(default = "Variables::default_warning_bg_color")]
268 pub warning_bg_color: Variable,
269 #[serde(default = "Variables::default_warning_fg_color")]
270 pub warning_fg_color: Variable,
271 #[serde(default = "Variables::default_error_color")]
272 pub error_color: Variable,
273 #[serde(default = "Variables::default_error_bg_color")]
274 pub error_bg_color: Variable,
275 #[serde(default = "Variables::default_error_fg_color")]
276 pub error_fg_color: Variable,
277 #[serde(default = "Variables::default_window_bg_color")]
278 pub window_bg_color: Variable,
279 #[serde(default = "Variables::default_window_fg_color")]
280 pub window_fg_color: Variable,
281 #[serde(default = "Variables::default_view_bg_color")]
282 pub view_bg_color: Variable,
283 #[serde(default = "Variables::default_view_fg_color")]
284 pub view_fg_color: Variable,
285 #[serde(default = "Variables::default_headerbar_bg_color")]
286 pub headerbar_bg_color: Variable,
287 #[serde(default = "Variables::default_headerbar_fg_color")]
288 pub headerbar_fg_color: Variable,
289 #[serde(default = "Variables::default_headerbar_border_color")]
290 pub headerbar_border_color: Variable,
291 #[serde(default = "Variables::default_headerbar_backdrop_color")]
292 pub headerbar_backdrop_color: Variable,
293 #[serde(default = "Variables::default_headerbar_shade_color")]
294 pub headerbar_shade_color: Variable,
295 #[serde(default = "Variables::default_headerbar_darker_shade_color")]
296 pub headerbar_darker_shade_color: Variable,
297 #[serde(default = "Variables::default_card_bg_color")]
298 pub card_bg_color: Variable,
299 #[serde(default = "Variables::default_card_fg_color")]
300 pub card_fg_color: Variable,
301 #[serde(default = "Variables::default_card_shade_color")]
302 pub card_shade_color: Variable,
303 #[serde(default = "Variables::default_dialog_bg_color")]
304 pub dialog_bg_color: Variable,
305 #[serde(default = "Variables::default_dialog_fg_color")]
306 pub dialog_fg_color: Variable,
307 #[serde(default = "Variables::default_popover_bg_color")]
308 pub popover_bg_color: Variable,
309 #[serde(default = "Variables::default_popover_fg_color")]
310 pub popover_fg_color: Variable,
311 #[serde(default = "Variables::default_popover_shade_color")]
312 pub popover_shade_color: Variable,
313 #[serde(default = "Variables::default_shade_color")]
314 pub shade_color: Variable,
315 #[serde(default = "Variables::default_scrollbar_outline_color")]
316 pub scrollbar_outline_color: Variable,
317 #[serde(default = "Variables::default_thumbnail_bg_color")]
318 pub thumbnail_bg_color: Variable,
319 #[serde(default = "Variables::default_thumbnail_fg_color")]
320 pub thumbnail_fg_color: Variable,
321 #[serde(default = "Variables::default_sidebar_bg_color")]
322 pub sidebar_bg_color: Variable,
323 #[serde(default = "Variables::default_sidebar_fg_color")]
324 pub sidebar_fg_color: Variable,
325 #[serde(default = "Variables::default_sidebar_backdrop_color")]
326 pub sidebar_backdrop_color: Variable,
327 #[serde(default = "Variables::default_sidebar_shade_color")]
328 pub sidebar_shade_color: Variable,
329 #[serde(default = "Variables::default_secondary_sidebar_bg_color")]
330 pub secondary_sidebar_bg_color: Variable,
331 #[serde(default = "Variables::default_secondary_sidebar_fg_color")]
332 pub secondary_sidebar_fg_color: Variable,
333 #[serde(default = "Variables::default_secondary_sidebar_backdrop_color")]
334 pub secondary_sidebar_backdrop_color: Variable,
335 #[serde(default = "Variables::default_secondary_sidebar_shade_color")]
336 pub secondary_sidebar_shade_color: Variable,
337}
338
339#[derive(Clone, Debug)]
340pub enum VariablesName {
341 AccentColor,
342 AccentBgColor,
343 AccentFgColor,
344 DestructiveColor,
345 DestructiveBgColor,
346 DestructiveFgColor,
347 SuccessColor,
348 SuccessBgColor,
349 SuccessFgColor,
350 WarningColor,
351 WarningBgColor,
352 WarningFgColor,
353 ErrorColor,
354 ErrorBgColor,
355 ErrorFgColor,
356 WindowBgColor,
357 WindowFgColor,
358 ViewBgColor,
359 ViewFgColor,
360 HeaderbarBgColor,
361 HeaderbarFgColor,
362 HeaderbarBorderColor,
363 HeaderbarBackdropColor,
364 HeaderbarShadeColor,
365 HeaderbarDarkerShadeColor,
366 CardBgColor,
367 CardFgColor,
368 CardShadeColor,
369 DialogBgColor,
370 DialogFgColor,
371 PopoverBgColor,
372 PopoverFgColor,
373 PopoverShadeColor,
374 ShadeColor,
375 ScrollbarOutlineColor,
376 ThumbnailBgColor,
377 ThumbnailFgColor,
378 SidebarBgColor,
379 SidebarFgColor,
380 SidebarBackdropColor,
381 SidebarShadeColor,
382 SecondarySidebarBgColor,
383 SecondarySidebarFgColor,
384 SecondarySidebarBackdropColor,
385 SecondarySidebarShadeColor,
386}
387
388impl VariablesName {
389 pub fn to_slug(&self) -> String {
390 match self {
391 VariablesName::AccentColor => "accent_color".to_string(),
392 VariablesName::AccentBgColor => "accent_bg_color".to_string(),
393 VariablesName::AccentFgColor => "accent_fg_color".to_string(),
394 VariablesName::DestructiveColor => "destructive_color".to_string(),
395 VariablesName::DestructiveBgColor => "destructive_bg_color".to_string(),
396 VariablesName::DestructiveFgColor => "destructive_fg_color".to_string(),
397 VariablesName::SuccessColor => "success_color".to_string(),
398 VariablesName::SuccessBgColor => "success_bg_color".to_string(),
399 VariablesName::SuccessFgColor => "success_fg_color".to_string(),
400 VariablesName::WarningColor => "warning_color".to_string(),
401 VariablesName::WarningBgColor => "warning_bg_color".to_string(),
402 VariablesName::WarningFgColor => "warning_fg_color".to_string(),
403 VariablesName::ErrorColor => "error_color".to_string(),
404 VariablesName::ErrorBgColor => "error_bg_color".to_string(),
405 VariablesName::ErrorFgColor => "error_fg_color".to_string(),
406 VariablesName::WindowBgColor => "window_bg_color".to_string(),
407 VariablesName::WindowFgColor => "window_fg_color".to_string(),
408 VariablesName::ViewBgColor => "view_bg_color".to_string(),
409 VariablesName::ViewFgColor => "view_fg_color".to_string(),
410 VariablesName::HeaderbarBgColor => "headerbar_bg_color".to_string(),
411 VariablesName::HeaderbarFgColor => "headerbar_fg_color".to_string(),
412 VariablesName::HeaderbarBorderColor => "headerbar_border_color".to_string(),
413 VariablesName::HeaderbarBackdropColor => "headerbar_backdrop_color".to_string(),
414 VariablesName::HeaderbarShadeColor => "headerbar_shade_color".to_string(),
415 VariablesName::HeaderbarDarkerShadeColor => "headerbar_darker_shade_color".to_string(),
416 VariablesName::CardBgColor => "card_bg_color".to_string(),
417 VariablesName::CardFgColor => "card_fg_color".to_string(),
418 VariablesName::CardShadeColor => "card_shade_color".to_string(),
419 VariablesName::DialogBgColor => "dialog_bg_color".to_string(),
420 VariablesName::DialogFgColor => "dialog_fg_color".to_string(),
421 VariablesName::PopoverBgColor => "popover_bg_color".to_string(),
422 VariablesName::PopoverFgColor => "popover_fg_color".to_string(),
423 VariablesName::PopoverShadeColor => "popover_shade_color".to_string(),
424 VariablesName::ShadeColor => "shade_color".to_string(),
425 VariablesName::ScrollbarOutlineColor => "scrollbar_outline_color".to_string(),
426 VariablesName::ThumbnailBgColor => "thumbnail_bg_color".to_string(),
427 VariablesName::ThumbnailFgColor => "thumbnail_fg_color".to_string(),
428 VariablesName::SidebarBgColor => "sidebar_bg_color".to_string(),
429 VariablesName::SidebarFgColor => "sidebar_fg_color".to_string(),
430 VariablesName::SidebarBackdropColor => "sidebar_backdrop_color".to_string(),
431 VariablesName::SidebarShadeColor => "sidebar_shade_color".to_string(),
432 VariablesName::SecondarySidebarBgColor => "secondary_sidebar_bg_color".to_string(),
433 VariablesName::SecondarySidebarFgColor => "secondary_sidebar_fg_color".to_string(),
434 VariablesName::SecondarySidebarBackdropColor => {
435 "secondary_sidebar_backdrop_color".to_string()
436 }
437 VariablesName::SecondarySidebarShadeColor => {
438 "secondary_sidebar_shade_color".to_string()
439 }
440 }
441 }
442}
443pub struct VariablesIterator<'a> {
444 variables: &'a Variables,
445 name: VariablesName,
446}
447
448impl<'a> std::iter::Iterator for VariablesIterator<'a> {
449 type Item = (VariablesName, &'a Variable);
450
451 fn next(&mut self) -> Option<Self::Item> {
452 let result = Some((self.name.clone(), self.variables.get(&self.name)));
453 self.name = match self.name {
454 VariablesName::AccentColor => VariablesName::AccentBgColor,
455 VariablesName::AccentBgColor => VariablesName::AccentFgColor,
456 VariablesName::AccentFgColor => VariablesName::DestructiveColor,
457 VariablesName::DestructiveColor => VariablesName::DestructiveBgColor,
458 VariablesName::DestructiveBgColor => VariablesName::DestructiveFgColor,
459 VariablesName::DestructiveFgColor => VariablesName::SuccessColor,
460 VariablesName::SuccessColor => VariablesName::SuccessBgColor,
461 VariablesName::SuccessBgColor => VariablesName::SuccessFgColor,
462 VariablesName::SuccessFgColor => VariablesName::WarningColor,
463 VariablesName::WarningColor => VariablesName::WarningBgColor,
464 VariablesName::WarningBgColor => VariablesName::WarningFgColor,
465 VariablesName::WarningFgColor => VariablesName::ErrorColor,
466 VariablesName::ErrorColor => VariablesName::ErrorBgColor,
467 VariablesName::ErrorBgColor => VariablesName::ErrorFgColor,
468 VariablesName::ErrorFgColor => VariablesName::WindowBgColor,
469 VariablesName::WindowBgColor => VariablesName::WindowFgColor,
470 VariablesName::WindowFgColor => VariablesName::ViewBgColor,
471 VariablesName::ViewBgColor => VariablesName::ViewFgColor,
472 VariablesName::ViewFgColor => VariablesName::HeaderbarBgColor,
473 VariablesName::HeaderbarBgColor => VariablesName::HeaderbarFgColor,
474 VariablesName::HeaderbarFgColor => VariablesName::HeaderbarBorderColor,
475 VariablesName::HeaderbarBorderColor => VariablesName::HeaderbarBackdropColor,
476 VariablesName::HeaderbarBackdropColor => VariablesName::HeaderbarShadeColor,
477 VariablesName::HeaderbarShadeColor => VariablesName::HeaderbarDarkerShadeColor,
478 VariablesName::HeaderbarDarkerShadeColor => VariablesName::CardBgColor,
479 VariablesName::CardBgColor => VariablesName::CardFgColor,
480 VariablesName::CardFgColor => VariablesName::CardShadeColor,
481 VariablesName::CardShadeColor => VariablesName::DialogBgColor,
482 VariablesName::DialogBgColor => VariablesName::DialogFgColor,
483 VariablesName::DialogFgColor => VariablesName::PopoverBgColor,
484 VariablesName::PopoverBgColor => VariablesName::PopoverFgColor,
485 VariablesName::PopoverFgColor => VariablesName::PopoverShadeColor,
486 VariablesName::PopoverShadeColor => VariablesName::ShadeColor,
487 VariablesName::ShadeColor => VariablesName::ScrollbarOutlineColor,
488 VariablesName::ScrollbarOutlineColor => VariablesName::ThumbnailBgColor,
489 VariablesName::ThumbnailBgColor => VariablesName::ThumbnailFgColor,
490 VariablesName::ThumbnailFgColor => VariablesName::SidebarBgColor,
491 VariablesName::SidebarBgColor => VariablesName::SidebarFgColor,
492 VariablesName::SidebarFgColor => VariablesName::SidebarBackdropColor,
493 VariablesName::SidebarBackdropColor => VariablesName::SidebarShadeColor,
494 VariablesName::SidebarShadeColor => VariablesName::SecondarySidebarBgColor,
495 VariablesName::SecondarySidebarBgColor => VariablesName::SecondarySidebarFgColor,
496 VariablesName::SecondarySidebarFgColor => VariablesName::SecondarySidebarBackdropColor,
497 VariablesName::SecondarySidebarBackdropColor => {
498 VariablesName::SecondarySidebarShadeColor
499 }
500 VariablesName::SecondarySidebarShadeColor => return None,
501 };
502 result
503 }
504}
505
506impl Variables {
507 pub fn get(&self, name: &VariablesName) -> &Variable {
508 match name {
509 VariablesName::AccentColor => &self.accent_color,
510 VariablesName::AccentBgColor => &self.accent_bg_color,
511 VariablesName::AccentFgColor => &self.accent_fg_color,
512 VariablesName::DestructiveColor => &self.destructive_color,
513 VariablesName::DestructiveBgColor => &self.destructive_bg_color,
514 VariablesName::DestructiveFgColor => &self.destructive_fg_color,
515
516 VariablesName::SuccessColor => &self.success_color,
517 VariablesName::SuccessBgColor => &self.success_bg_color,
518 VariablesName::SuccessFgColor => &self.success_fg_color,
519
520 VariablesName::WarningColor => &self.warning_color,
521 VariablesName::WarningBgColor => &self.warning_bg_color,
522 VariablesName::WarningFgColor => &self.warning_fg_color,
523
524 VariablesName::ErrorColor => &self.error_color,
525 VariablesName::ErrorBgColor => &self.error_bg_color,
526 VariablesName::ErrorFgColor => &self.error_fg_color,
527
528 VariablesName::WindowBgColor => &self.window_bg_color,
529 VariablesName::WindowFgColor => &self.window_fg_color,
530
531 VariablesName::ViewBgColor => &self.view_bg_color,
532 VariablesName::ViewFgColor => &self.view_fg_color,
533
534 VariablesName::HeaderbarBgColor => &self.headerbar_bg_color,
535 VariablesName::HeaderbarFgColor => &self.headerbar_fg_color,
536 VariablesName::HeaderbarBorderColor => &self.headerbar_border_color,
537 VariablesName::HeaderbarBackdropColor => &self.headerbar_backdrop_color,
538 VariablesName::HeaderbarShadeColor => &self.headerbar_shade_color,
539 VariablesName::HeaderbarDarkerShadeColor => &self.headerbar_darker_shade_color,
540
541 VariablesName::CardBgColor => &self.card_bg_color,
542 VariablesName::CardFgColor => &self.card_fg_color,
543 VariablesName::CardShadeColor => &self.card_shade_color,
544
545 VariablesName::DialogBgColor => &self.dialog_bg_color,
546 VariablesName::DialogFgColor => &self.dialog_fg_color,
547
548 VariablesName::PopoverBgColor => &self.popover_bg_color,
549 VariablesName::PopoverFgColor => &self.popover_fg_color,
550 VariablesName::PopoverShadeColor => &self.popover_shade_color,
551
552 VariablesName::ShadeColor => &self.shade_color,
553 VariablesName::ScrollbarOutlineColor => &self.scrollbar_outline_color,
554
555 VariablesName::ThumbnailBgColor => &self.thumbnail_bg_color,
556 VariablesName::ThumbnailFgColor => &self.thumbnail_fg_color,
557
558 VariablesName::SidebarBgColor => &self.sidebar_bg_color,
559 VariablesName::SidebarFgColor => &self.sidebar_fg_color,
560 VariablesName::SidebarBackdropColor => &self.sidebar_backdrop_color,
561 VariablesName::SidebarShadeColor => &self.sidebar_shade_color,
562
563 VariablesName::SecondarySidebarBgColor => &self.secondary_sidebar_bg_color,
564 VariablesName::SecondarySidebarFgColor => &self.secondary_sidebar_fg_color,
565 VariablesName::SecondarySidebarBackdropColor => &self.secondary_sidebar_backdrop_color,
566 VariablesName::SecondarySidebarShadeColor => &self.secondary_sidebar_shade_color,
567 }
568 }
569
570 pub fn iter(&self) -> VariablesIterator {
571 VariablesIterator {
572 variables: self,
573 name: VariablesName::AccentColor,
574 }
575 }
576
577 fn default_accent_color() -> Variable {
578 Variable::Mode {
579 light: "#3584e4".to_string(),
580 dark: "#78aeed".to_string(),
581 }
582 }
583
584 fn default_accent_bg_color() -> Variable {
585 Variable::Mode {
586 light: "#3584e4".to_string(),
587 dark: "#3584e4".to_string(),
588 }
589 }
590
591 fn default_accent_fg_color() -> Variable {
592 Variable::Mode {
593 light: "#ffffff".to_string(),
594 dark: "#ffffff".to_string(),
595 }
596 }
597
598 fn default_destructive_color() -> Variable {
599 Variable::Mode {
600 light: "#c01c28".to_string(),
601 dark: "#ff7b63".to_string(),
602 }
603 }
604
605 fn default_destructive_bg_color() -> Variable {
606 Variable::Mode {
607 light: "#e01b24".to_string(),
608 dark: "#c01c28".to_string(),
609 }
610 }
611
612 fn default_destructive_fg_color() -> Variable {
613 Variable::Mode {
614 light: "#ffffff".to_string(),
615 dark: "#ffffff".to_string(),
616 }
617 }
618
619 fn default_success_color() -> Variable {
620 Variable::Mode {
621 light: "#26a269".to_string(),
622 dark: "#8ff0a4".to_string(),
623 }
624 }
625
626 fn default_success_bg_color() -> Variable {
627 Variable::Mode {
628 light: "#2ec27e".to_string(),
629 dark: "#26a269".to_string(),
630 }
631 }
632
633 fn default_success_fg_color() -> Variable {
634 Variable::Mode {
635 light: "#ffffff".to_string(),
636 dark: "#ffffff".to_string(),
637 }
638 }
639
640 fn default_warning_color() -> Variable {
641 Variable::Mode {
642 light: "#ae7b03".to_string(),
643 dark: "#f8e45c".to_string(),
644 }
645 }
646
647 fn default_warning_bg_color() -> Variable {
648 Variable::Mode {
649 light: "#e5a50a".to_string(),
650 dark: "#cd9309".to_string(),
651 }
652 }
653
654 fn default_warning_fg_color() -> Variable {
655 Variable::Mode {
656 light: "rgba(0, 0, 0, 0.8)".to_string(),
657 dark: "rgba(0, 0, 0, 0.8)".to_string(),
658 }
659 }
660
661 fn default_error_color() -> Variable {
662 Variable::Mode {
663 light: "#c01c28".to_string(),
664 dark: "#ff7b63".to_string(),
665 }
666 }
667
668 fn default_error_bg_color() -> Variable {
669 Variable::Mode {
670 light: "#e01b24".to_string(),
671 dark: "#c01c28".to_string(),
672 }
673 }
674
675 fn default_error_fg_color() -> Variable {
676 Variable::Mode {
677 light: "#ffffff".to_string(),
678 dark: "#ffffff".to_string(),
679 }
680 }
681
682 fn default_window_bg_color() -> Variable {
683 Variable::Mode {
684 light: "#fafafa".to_string(),
685 dark: "#242424".to_string(),
686 }
687 }
688
689 fn default_window_fg_color() -> Variable {
690 Variable::Mode {
691 light: "rgba(0, 0, 0, 0.8)".to_string(),
692 dark: "#ffffff".to_string(),
693 }
694 }
695
696 fn default_view_bg_color() -> Variable {
697 Variable::Mode {
698 light: "#ffffff".to_string(),
699 dark: "#1e1e1e".to_string(),
700 }
701 }
702
703 fn default_view_fg_color() -> Variable {
704 Variable::Mode {
705 light: "rgba(0, 0, 0, 0.8)".to_string(),
706 dark: "#ffffff".to_string(),
707 }
708 }
709
710 fn default_headerbar_bg_color() -> Variable {
711 Variable::Mode {
712 light: "#ebebeb".to_string(),
713 dark: "#303030".to_string(),
714 }
715 }
716
717 fn default_headerbar_fg_color() -> Variable {
718 Variable::Mode {
719 light: "rgba(0, 0, 0, 0.8)".to_string(),
720 dark: "#ffffff".to_string(),
721 }
722 }
723
724 fn default_headerbar_border_color() -> Variable {
725 Variable::Mode {
726 light: "rgba(0, 0, 0, 0.8)".to_string(),
727 dark: "#ffffff".to_string(),
728 }
729 }
730
731 fn default_headerbar_backdrop_color() -> Variable {
732 Variable::Mode {
733 light: "@window_bg_color".to_string(),
734 dark: "@window_bg_color".to_string(),
735 }
736 }
737
738 fn default_headerbar_shade_color() -> Variable {
739 Variable::Mode {
740 light: "rgba(0, 0, 0, 0.07)".to_string(),
741 dark: "rgba(0, 0, 0, 0.36)".to_string(),
742 }
743 }
744
745 fn default_headerbar_darker_shade_color() -> Variable {
746 Variable::Mode {
747 light: "rgba(0, 0, 0, 0.12)".to_string(),
748 dark: "rgba(0, 0, 0, 0.9)".to_string(),
749 }
750 }
751
752 fn default_card_bg_color() -> Variable {
753 Variable::Mode {
754 light: "#ffffff".to_string(),
755 dark: "rgba(255, 255, 255, 0.08)".to_string(),
756 }
757 }
758
759 fn default_card_fg_color() -> Variable {
760 Variable::Mode {
761 light: "rgba(0, 0, 0, 0.8)".to_string(),
762 dark: "#ffffff".to_string(),
763 }
764 }
765
766 fn default_card_shade_color() -> Variable {
767 Variable::Mode {
768 light: "rgba(0, 0, 0, 0.07)".to_string(),
769 dark: "rgba(0, 0, 0, 0.36)".to_string(),
770 }
771 }
772
773 fn default_dialog_bg_color() -> Variable {
774 Variable::Mode {
775 light: "#fafafa".to_string(),
776 dark: "#383838".to_string(),
777 }
778 }
779
780 fn default_dialog_fg_color() -> Variable {
781 Variable::Mode {
782 light: "rgba(0, 0, 0, 0.8)".to_string(),
783 dark: "#ffffff".to_string(),
784 }
785 }
786
787 fn default_popover_bg_color() -> Variable {
788 Variable::Mode {
789 light: "#ffffff".to_string(),
790 dark: "#383838".to_string(),
791 }
792 }
793
794 fn default_popover_fg_color() -> Variable {
795 Variable::Mode {
796 light: "rgba(0, 0, 0, 0.8)".to_string(),
797 dark: "#ffffff".to_string(),
798 }
799 }
800
801 fn default_popover_shade_color() -> Variable {
802 Variable::Mode {
803 light: "rgba(0, 0, 0, 0.07)".to_string(),
804 dark: "rgba(0, 0, 0, 0.36)".to_string(),
805 }
806 }
807
808 fn default_shade_color() -> Variable {
809 Variable::Mode {
810 light: "rgba(0, 0, 0, 0.07)".to_string(),
811 dark: "rgba(0, 0, 0, 0.36)".to_string(),
812 }
813 }
814
815 fn default_scrollbar_outline_color() -> Variable {
816 Variable::Mode {
817 light: "#ffffff".to_string(),
818 dark: "rgba(0, 0, 0, 0.5)".to_string(),
819 }
820 }
821
822 fn default_thumbnail_bg_color() -> Variable {
823 Variable::Mode {
824 light: "#ffffff".to_string(),
825 dark: "#383838".to_string(),
826 }
827 }
828
829 fn default_thumbnail_fg_color() -> Variable {
830 Variable::Mode {
831 light: "rgba(0, 0, 0, 0.8)".to_string(),
832 dark: "#ffffff".to_string(),
833 }
834 }
835
836 fn default_sidebar_bg_color() -> Variable {
837 Variable::Mode {
838 light: "#ebebeb".to_string(),
839 dark: "#303030".to_string(),
840 }
841 }
842
843 fn default_sidebar_fg_color() -> Variable {
844 Variable::Mode {
845 light: "rgba(0, 0, 0, 0.8)".to_string(),
846 dark: "#ffffff".to_string(),
847 }
848 }
849
850 fn default_sidebar_backdrop_color() -> Variable {
851 Variable::Mode {
852 light: "#f2f2f2".to_string(),
853 dark: "#2a2a2a".to_string(),
854 }
855 }
856
857 fn default_sidebar_shade_color() -> Variable {
858 Variable::Mode {
859 light: "rgba(0, 0, 0, 0.07)".to_string(),
860 dark: "rgba(0, 0, 0, 0.36)".to_string(),
861 }
862 }
863
864 fn default_secondary_sidebar_bg_color() -> Variable {
865 Variable::Mode {
866 light: "#f3f3f3".to_string(),
867 dark: "#2a2a2a".to_string(),
868 }
869 }
870
871 fn default_secondary_sidebar_fg_color() -> Variable {
872 Variable::Mode {
873 light: "rgba(0, 0, 0, 0.8)".to_string(),
874 dark: "#ffffff".to_string(),
875 }
876 }
877
878 fn default_secondary_sidebar_backdrop_color() -> Variable {
879 Variable::Mode {
880 light: "#f6f6f6".to_string(),
881 dark: "#272727".to_string(),
882 }
883 }
884
885 fn default_secondary_sidebar_shade_color() -> Variable {
886 Variable::Mode {
887 light: "rgba(0, 0, 0, 0.07)".to_string(),
888 dark: "rgba(0, 0, 0, 0.36)".to_string(),
889 }
890 }
891}
892
893#[derive(Serialize, Deserialize, Debug, Clone)]
894#[serde(untagged)]
895pub enum Variable {
896 Single(String),
897 Mode {
898 light: String,
899 dark: String,
900 },
901 Accent {
902 #[serde(default)]
903 blue: String,
904 #[serde(default)]
905 green: String,
906 #[serde(default)]
907 red: String,
908 #[serde(default)]
909 yellow: String,
910 #[serde(default)]
911 purple: String,
912 #[serde(default)]
913 pink: String,
914 #[serde(default)]
915 orange: String,
916 #[serde(default)]
917 slate: String,
918 #[serde(default)]
919 teal: String,
920 default: String,
921 },
922 ModeAccent {
923 light: HashMap<String, String>,
924 dark: HashMap<String, String>,
925 },
926}
927
928impl Variable {
929 pub fn get(&self, mode: &str, accent: &str) -> String {
930 match self {
931 Variable::Single(s) => s.to_string(),
932 Variable::Mode { light, dark } => {
933 if mode == "light" {
934 light.to_string()
935 } else {
936 dark.to_string()
937 }
938 }
939 Variable::Accent {
940 blue,
941 green,
942 red,
943 yellow,
944 purple,
945 pink,
946 orange,
947 slate,
948 teal,
949 default,
950 } => match accent {
951 "blue" => {
952 if blue.is_empty() {
953 default.to_string()
954 } else {
955 blue.to_string()
956 }
957 }
958 "green" => {
959 if green.is_empty() {
960 default.to_string()
961 } else {
962 green.to_string()
963 }
964 }
965 "red" => {
966 if red.is_empty() {
967 default.to_string()
968 } else {
969 red.to_string()
970 }
971 }
972 "yellow" => {
973 if yellow.is_empty() {
974 default.to_string()
975 } else {
976 yellow.to_string()
977 }
978 }
979 "purple" => {
980 if purple.is_empty() {
981 default.to_string()
982 } else {
983 purple.to_string()
984 }
985 }
986 "pink" => {
987 if pink.is_empty() {
988 default.to_string()
989 } else {
990 pink.to_string()
991 }
992 }
993 "orange" => {
994 if orange.is_empty() {
995 default.to_string()
996 } else {
997 orange.to_string()
998 }
999 }
1000 "slate" => {
1001 if slate.is_empty() {
1002 default.to_string()
1003 } else {
1004 slate.to_string()
1005 }
1006 }
1007 "teal" => {
1008 if teal.is_empty() {
1009 default.to_string()
1010 } else {
1011 teal.to_string()
1012 }
1013 }
1014 _ => default.to_string(),
1015 },
1016 Variable::ModeAccent { light, dark } => {
1017 if mode == "light" {
1018 if light.contains_key(accent) {
1019 light[accent].to_string()
1020 } else {
1021 light["default"].to_string()
1022 }
1023 } else {
1024 if dark.contains_key(accent) {
1025 dark[accent].to_string()
1026 } else {
1027 dark["default"].to_string()
1028 }
1029 }
1030 }
1031 }
1032 }
1033}
1034
1035#[derive(Debug, Clone, Serialize, Deserialize)]
1036pub struct Palette {
1037 #[serde(alias = "blue_")]
1038 pub blue: HashMap<String, String>,
1039 #[serde(alias = "green_")]
1040 pub green: HashMap<String, String>,
1041 #[serde(alias = "yellow_")]
1042 pub yellow: HashMap<String, String>,
1043 #[serde(alias = "orange_")]
1044 pub orange: HashMap<String, String>,
1045 #[serde(alias = "red_")]
1046 pub red: HashMap<String, String>,
1047 #[serde(alias = "purple_")]
1048 pub purple: HashMap<String, String>,
1049 #[serde(alias = "brown_")]
1050 pub brown: HashMap<String, String>,
1051 #[serde(alias = "light_")]
1052 pub light: HashMap<String, String>,
1053 #[serde(alias = "dark_")]
1054 pub dark: HashMap<String, String>,
1055}
1056
1057#[derive(Debug, Clone, Serialize, Deserialize)]
1058pub struct Custom {
1059 #[serde(default)]
1060 pub gtk4: String,
1061 #[serde(default)]
1062 pub gtk3: String,
1063 #[serde(default)]
1064 pub shell: String,
1065}
1066
1067impl Preset {
1068 pub fn from_str(s: &str) -> Preset {
1069 let p: Preset = serde_json::from_str(s).unwrap();
1070 p
1071 }
1072
1073 pub fn to_str(&self) -> String {
1074 serde_json::to_string(self).unwrap()
1075 }
1076
1077 pub fn from_file(file: &str) -> Preset {
1078 let s = std::fs::read_to_string(file).unwrap();
1079 Preset::from_str(&s)
1080 }
1081
1082 pub fn to_file(&self, file: &str) {
1083 let s = self.to_str();
1084 std::fs::write(file, s).unwrap();
1085 }
1086
1087 pub fn to_css(&self, mode: Mode, accent: AccentsColor, gtk: GtkVersion) -> String {
1088 let mut css = match gtk {
1089 GtkVersion::Gtk3 => self.custom.gtk3.clone(),
1090 GtkVersion::Gtk4 => self.custom.gtk4.clone(),
1091 };
1092
1093 let mode = match mode {
1094 Mode::Light => "light",
1095 Mode::Dark => "dark",
1096 };
1097
1098 let accent = match accent {
1099 AccentsColor::Blue => "blue",
1100 AccentsColor::Green => "green",
1101 AccentsColor::Yellow => "yellow",
1102 AccentsColor::Orange => "orange",
1103 AccentsColor::Red => "red",
1104 AccentsColor::Purple => "purple",
1105 AccentsColor::Pink => "pink",
1106 AccentsColor::Slate => "slate",
1107 AccentsColor::Teal => "teal",
1108 };
1109
1110 css += &format!(
1111 "/* Preset: {}@{} {}/{} */\n",
1112 self.name, self.version, mode, accent
1113 );
1114
1115 for (name, variable) in self.variables.iter() {
1116 let value = variable.get(mode, accent);
1117 css += &format!("@define-color {} {}\n", name.to_slug(), value);
1118 }
1119
1120 for (name, value) in self.palette.blue.iter() {
1121 css += &format!("@define-color blue_{} {}\n", name, value);
1122 }
1123
1124 for (name, value) in self.palette.green.iter() {
1125 css += &format!("@define-color green_{} {}\n", name, value);
1126 }
1127
1128 for (name, value) in self.palette.yellow.iter() {
1129 css += &format!("@define-color yellow_{} {}\n", name, value);
1130 }
1131
1132 for (name, value) in self.palette.orange.iter() {
1133 css += &format!("@define-color orange_{} {}\n", name, value);
1134 }
1135
1136 for (name, value) in self.palette.red.iter() {
1137 css += &format!("@define-color red_{} {}\n", name, value);
1138 }
1139
1140 for (name, value) in self.palette.purple.iter() {
1141 css += &format!("@define-color purple_{} {}\n", name, value);
1142 }
1143
1144 for (name, value) in self.palette.brown.iter() {
1145 css += &format!("@define-color brown_{} {}\n", name, value);
1146 }
1147
1148 for (name, value) in self.palette.light.iter() {
1149 css += &format!("@define-color light_{} {}\n", name, value);
1150 }
1151
1152 for (name, value) in self.palette.dark.iter() {
1153 css += &format!("@define-color dark_{} {}\n", name, value);
1154 }
1155
1156 css = css.replace("@mode", mode);
1157 css = css.replace("@accent", accent);
1158 css
1159 }
1160
1161 pub fn render_template(&self, template: String, mode: Mode, accent: AccentsColor) -> String {
1162 let reg = Handlebars::new();
1163
1164 let mode = match mode {
1165 Mode::Light => "light",
1166 Mode::Dark => "dark",
1167 };
1168
1169 let accent = match accent {
1170 AccentsColor::Blue => "blue",
1171 AccentsColor::Green => "green",
1172 AccentsColor::Yellow => "yellow",
1173 AccentsColor::Orange => "orange",
1174 AccentsColor::Red => "red",
1175 AccentsColor::Purple => "purple",
1176 AccentsColor::Pink => "pink",
1177 AccentsColor::Slate => "slate",
1178 AccentsColor::Teal => "teal",
1179 };
1180
1181 let mut data = BTreeMap::new();
1182 data.insert(
1183 "bg_color".to_string(),
1184 self.shell.bg_color.get(mode, accent),
1185 );
1186 data.insert(
1187 "fg_color".to_string(),
1188 self.shell.fg_color.get(mode, accent),
1189 );
1190 data.insert(
1191 "system_bg_color".to_string(),
1192 self.shell.system_bg_color.get(mode, accent),
1193 );
1194 data.insert(
1195 "system_fg_color".to_string(),
1196 self.shell.system_fg_color.get(mode, accent),
1197 );
1198 data.insert(
1199 "selected_bg_color".to_string(),
1200 self.shell.selected_bg_color.get(mode, accent),
1201 );
1202 data.insert(
1203 "selected_fg_color".to_string(),
1204 self.shell.selected_fg_color.get(mode, accent),
1205 );
1206 data.insert(
1207 "panel_bg_color".to_string(),
1208 self.shell.panel_bg_color.get(mode, accent),
1209 );
1210 data.insert(
1211 "panel_fg_color".to_string(),
1212 self.shell.panel_fg_color.get(mode, accent),
1213 );
1214 data.insert(
1215 "osd_bg_color".to_string(),
1216 self.shell.osd_bg_color.get(mode, accent),
1217 );
1218 data.insert(
1219 "osd_fg_color".to_string(),
1220 self.shell.osd_fg_color.get(mode, accent),
1221 );
1222
1223 data.insert("name".to_string(), self.name.clone());
1224 data.insert("version".to_string(), self.version.clone());
1225
1226 for (name, variable) in self.variables.iter() {
1228 data.insert(name.to_slug(), variable.get(mode, accent));
1229 }
1230
1231 for (name, value) in self.palette.blue.iter() {
1233 data.insert(format!("blue_{}", name), value.to_string());
1234 }
1235
1236 for (name, value) in self.palette.green.iter() {
1237 data.insert(format!("green_{}", name), value.to_string());
1238 }
1239
1240 for (name, value) in self.palette.yellow.iter() {
1241 data.insert(format!("yellow_{}", name), value.to_string());
1242 }
1243
1244 for (name, value) in self.palette.orange.iter() {
1245 data.insert(format!("orange_{}", name), value.to_string());
1246 }
1247
1248 for (name, value) in self.palette.red.iter() {
1249 data.insert(format!("red_{}", name), value.to_string());
1250 }
1251
1252 for (name, value) in self.palette.purple.iter() {
1253 data.insert(format!("purple_{}", name), value.to_string());
1254 }
1255
1256 for (name, value) in self.palette.brown.iter() {
1257 data.insert(format!("brown_{}", name), value.to_string());
1258 }
1259
1260 for (name, value) in self.palette.light.iter() {
1261 data.insert(format!("light_{}", name), value.to_string());
1262 }
1263
1264 for (name, value) in self.palette.dark.iter() {
1265 data.insert(format!("dark_{}", name), value.to_string());
1266 }
1267
1268 data.insert("custom_css".to_string(), self.custom.shell.clone());
1269 data.insert("mode".to_string(), mode.to_string());
1270 data.insert("accent".to_string(), accent.to_string());
1271
1272 let mut result = reg.render_template(&template, &data).unwrap();
1273
1274 result = result.replace("@mode", mode);
1275 result = result.replace("@accent", &accent.to_string());
1276 result
1277 }
1278}
1279
1280pub struct ApplyBuilder {
1281 preset: Preset,
1282 gtk3_path: String,
1283 gtk4_path: String,
1284 mode: Mode,
1285 accent: AccentsColor,
1286}
1287
1288impl ApplyBuilder {
1289 pub fn new(preset: Preset) -> ApplyBuilder {
1290 ApplyBuilder {
1291 preset,
1292 gtk3_path: "".to_string(),
1293 gtk4_path: "".to_string(),
1294 mode: Mode::Light,
1295 accent: AccentsColor::Blue,
1296 }
1297 }
1298
1299 pub fn gtk3_path(mut self, path: &str) -> ApplyBuilder {
1300 self.gtk3_path = path.to_string();
1301 self
1302 }
1303
1304 pub fn gtk4_path(mut self, path: &str) -> ApplyBuilder {
1305 self.gtk4_path = path.to_string();
1306 self
1307 }
1308
1309 pub fn mode(mut self, mode: Mode) -> ApplyBuilder {
1310 self.mode = mode;
1311 self
1312 }
1313
1314 pub fn accent(mut self, accent: AccentsColor) -> ApplyBuilder {
1315 self.accent = accent;
1316 self
1317 }
1318
1319 pub fn apply(&self) {
1320 if !self.gtk3_path.is_empty() {
1322 let path = std::path::Path::new(&self.gtk3_path);
1323 if let Some(parent) = path.parent() {
1324 if !parent.exists() {
1325 std::fs::create_dir_all(parent).unwrap();
1326 }
1327 }
1328 }
1329
1330 if !self.gtk4_path.is_empty() {
1331 let path = std::path::Path::new(&self.gtk4_path);
1332 if let Some(parent) = path.parent() {
1333 if !parent.exists() {
1334 std::fs::create_dir_all(parent).unwrap();
1335 }
1336 }
1337 }
1338
1339 let css = self.preset.to_css(self.mode, self.accent, GtkVersion::Gtk3);
1340 std::fs::write(&self.gtk3_path, css).unwrap();
1341 let css = self.preset.to_css(self.mode, self.accent, GtkVersion::Gtk4);
1342 std::fs::write(&self.gtk4_path, css).unwrap();
1343 }
1344}