gradience_lib/
preset.rs

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        // insert all variables
1227        for (name, variable) in self.variables.iter() {
1228            data.insert(name.to_slug(), variable.get(mode, accent));
1229        }
1230
1231        // insert all palette colors
1232        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        // check if the paths exists, if not try to make directories
1321        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}