Skip to main content

gecol_core/theme/
theme_struct.rs

1use std::fmt::Display;
2
3use palette::{IntoColor, Oklch, Srgb};
4use serde::{Deserialize, Serialize};
5
6use crate::theme::{Color, ThemeType};
7
8/// Represents generated theme containing different colors.
9///
10/// It is used to generated a theme from the given color and then contain
11/// the generated colors.
12///
13/// # Example
14///
15/// ```rust
16/// use gecol_core::theme::Theme;
17///
18/// // Generates dark theme
19/// let theme = Theme::dark((155, 155, 0));
20/// ```
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct Theme {
23    pub primary: Color,
24    pub secondary: Color,
25
26    pub background: Color,
27    pub surface: Color,
28    pub border: Color,
29
30    pub foreground: Color,
31    pub muted: Color,
32
33    pub success: Color,
34    pub warning: Color,
35    pub error: Color,
36}
37
38impl Theme {
39    /// Generates the theme based on the given color..
40    ///
41    /// It generates based on the given theme type. The given color will be
42    /// the primary color and the other colors willbe generated based on that
43    /// color.
44    pub fn generate(theme_type: ThemeType, rgb: (u8, u8, u8)) -> Self {
45        match theme_type {
46            ThemeType::Dark => Self::dark(rgb),
47            ThemeType::Light => Self::light(rgb),
48        }
49    }
50
51    /// Generates dark theme based on the given color.
52    ///
53    /// Given color will be the primary color and the other colors will be
54    /// generated based on that color.
55    pub fn dark((r, g, b): (u8, u8, u8)) -> Self {
56        let color: Oklch =
57            Srgb::new(r as f32 / 255., g as f32 / 255., b as f32 / 255.)
58                .into_color();
59
60        let (secondary, success, warning, error) = Self::shared_colors(color);
61        Self {
62            primary: Color(color),
63            secondary: Color(secondary),
64            background: Color(Oklch::new(0.20, 0.006, color.hue)),
65            surface: Color(Oklch::new(0.25, 0.008, color.hue)),
66            border: Color(Oklch::new(0.32, 0.010, color.hue)),
67            foreground: Color(Oklch::new(0.9, 0.01, color.hue)),
68            muted: Color(Oklch::new(0.65, 0.02, color.hue)),
69            success: Color(success),
70            warning: Color(warning),
71            error: Color(error),
72        }
73    }
74
75    /// Generates light theme based on the given color.
76    ///
77    /// Given color will be the primary color and the other colors will be
78    /// generated based on that color. It restrains the maximum primary color
79    /// lightness in order for it to be visible.
80    pub fn light((r, g, b): (u8, u8, u8)) -> Self {
81        let mut color: Oklch =
82            Srgb::new(r as f32 / 255., g as f32 / 255., b as f32 / 255.)
83                .into_color();
84
85        color.l = color.l.min(0.70);
86        let (secondary, success, warning, error) = Self::shared_colors(color);
87        Self {
88            primary: Color(color),
89            secondary: Color(secondary),
90            background: Color(Oklch::new(0.98, 0.004, color.hue)),
91            surface: Color(Oklch::new(0.94, 0.006, color.hue)),
92            border: Color(Oklch::new(0.85, 0.010, color.hue)),
93            foreground: Color(Oklch::new(0.15, 0.01, color.hue)),
94            muted: Color(Oklch::new(0.45, 0.02, color.hue)),
95            success: Color(success),
96            warning: Color(warning),
97            error: Color(error),
98        }
99    }
100
101    fn shared_colors(color: Oklch) -> (Oklch, Oklch, Oklch, Oklch) {
102        let mut secondary = color;
103        secondary.hue += 35.;
104
105        let mut success = color;
106        success.hue = 140.0.into();
107
108        let mut warning = color;
109        warning.hue = 100.0.into();
110
111        let mut error = color;
112        error.hue = 25.0.into();
113
114        (secondary, success, warning, error)
115    }
116}
117
118impl Display for Theme {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        writeln!(f, "Primary:    {}  \x1b[0m", self.primary)?;
121        writeln!(f, "Secondary:  {}  \x1b[0m", self.secondary)?;
122        writeln!(f, "Background: {}  \x1b[0m", self.background)?;
123        writeln!(f, "Surface:    {}  \x1b[0m", self.surface)?;
124        writeln!(f, "Border:     {}  \x1b[0m", self.border)?;
125        writeln!(f, "Foreground: {}  \x1b[0m", self.foreground)?;
126        writeln!(f, "Muted:      {}  \x1b[0m", self.muted)?;
127        writeln!(f, "Success:    {}  \x1b[0m", self.success)?;
128        writeln!(f, "Warning:    {}  \x1b[0m", self.warning)?;
129        write!(f, "Error:      {}  \x1b[0m", self.error)
130    }
131}