cosmic_theme/model/
derivation.rs

1use palette::{Srgba, WithAlpha};
2use serde::{Deserialize, Serialize};
3
4use crate::composite::over;
5
6/// Theme Container colors of a theme, can be a theme background container, primary container, or secondary container
7#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
8#[must_use]
9pub struct Container {
10    /// the color of the container
11    pub base: Srgba,
12    /// the color of components in the container
13    pub component: Component,
14    /// the color of dividers in the container
15    pub divider: Srgba,
16    /// the color of text in the container
17    pub on: Srgba,
18    /// the color of @small_widget_container
19    pub small_widget: Srgba,
20}
21
22impl Container {
23    pub(crate) fn new(
24        component: Component,
25        base: Srgba,
26        on: Srgba,
27        mut small_widget: Srgba,
28        is_high_contrast: bool,
29    ) -> Self {
30        let divider_c = on.with_alpha(if is_high_contrast { 0.5 } else { 0.2 });
31        small_widget.alpha = 0.25;
32
33        Self {
34            base,
35            component,
36            divider: over(divider_c, base),
37            on,
38            small_widget,
39        }
40    }
41}
42
43/// The colors for a widget of the Cosmic theme
44#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
45#[must_use]
46pub struct Component {
47    /// The base color of the widget
48    pub base: Srgba,
49    /// The color of the widget when it is hovered
50    pub hover: Srgba,
51    /// the color of the widget when it is pressed
52    pub pressed: Srgba,
53    /// the color of the widget when it is selected
54    pub selected: Srgba,
55    /// the color of the widget when it is selected
56    pub selected_text: Srgba,
57    /// the color of the widget when it is focused
58    pub focus: Srgba,
59    /// the color of dividers for this widget
60    pub divider: Srgba,
61    /// the color of text for this widget
62    pub on: Srgba,
63    // the color of text with opacity 80 for this widget
64    // pub text_opacity_80: Srgba,
65    /// the color of the widget when it is disabled
66    pub disabled: Srgba,
67    /// the color of text in the widget when it is disabled
68    pub on_disabled: Srgba,
69    /// the color of the border for the widget
70    pub border: Srgba,
71    /// the color of the border for the widget when it is disabled
72    pub disabled_border: Srgba,
73}
74
75#[allow(clippy::must_use_candidate)]
76#[allow(clippy::doc_markdown)]
77impl Component {
78    #[inline]
79    /// get @hover_state_color
80    pub fn hover_state_color(&self) -> Srgba {
81        self.hover
82    }
83
84    #[inline]
85    /// get @pressed_state_color
86    pub fn pressed_state_color(&self) -> Srgba {
87        self.pressed
88    }
89
90    #[inline]
91    /// get @selected_state_color
92    pub fn selected_state_color(&self) -> Srgba {
93        self.selected
94    }
95
96    #[inline]
97    /// get @selected_state_text_color
98    pub fn selected_state_text_color(&self) -> Srgba {
99        self.selected_text
100    }
101
102    #[inline]
103    /// get @focus_color
104    pub fn focus_color(&self) -> Srgba {
105        self.focus
106    }
107
108    /// helper for producing a component from a base color a neutral and an accent
109    pub fn colored_component(
110        base: Srgba,
111        neutral: Srgba,
112        accent: Srgba,
113        hovered: Srgba,
114        pressed: Srgba,
115    ) -> Self {
116        let mut base_50 = base;
117        base_50.alpha *= 0.5;
118
119        let on_20 = neutral;
120        let on_50 = on_20.with_alpha(0.5);
121
122        Component {
123            base,
124            hover: over(hovered, base),
125            pressed: over(pressed, base),
126            selected: over(hovered, base),
127            selected_text: accent,
128            divider: on_20,
129            on: neutral,
130            disabled: over(base_50, base),
131            on_disabled: over(on_50, base),
132            focus: accent,
133            border: base,
134            disabled_border: base_50,
135        }
136    }
137
138    /// helper for producing a button component
139    pub fn colored_button(
140        base: Srgba,
141        overlay: Srgba,
142        on_button: Srgba,
143        accent: Srgba,
144        hovered: Srgba,
145        pressed: Srgba,
146    ) -> Self {
147        let mut component = Component::colored_component(base, overlay, accent, hovered, pressed);
148        component.on = on_button;
149
150        let on_disabled = on_button.with_alpha(0.5);
151        component.on_disabled = on_disabled;
152
153        component
154    }
155
156    /// helper for producing a component color theme
157    #[allow(clippy::self_named_constructors)]
158    pub fn component(
159        base: Srgba,
160        accent: Srgba,
161        on_component: Srgba,
162        hovered: Srgba,
163        pressed: Srgba,
164        is_high_contrast: bool,
165        border: Srgba,
166    ) -> Self {
167        let mut base_50 = base;
168        base_50.alpha *= 0.5;
169
170        let on_20 = on_component.with_alpha(0.2);
171        let on_65 = on_20.with_alpha(0.65);
172
173        let mut disabled_border = border;
174        disabled_border.alpha *= 0.5;
175
176        Component {
177            base,
178            hover: if base.alpha < 0.001 {
179                hovered
180            } else {
181                over(hovered, base)
182            },
183            pressed: if base.alpha < 0.001 {
184                pressed
185            } else {
186                over(pressed, base)
187            },
188            selected: if base.alpha < 0.001 {
189                hovered
190            } else {
191                over(hovered, base)
192            },
193            selected_text: accent,
194            focus: accent,
195            divider: if is_high_contrast { on_65 } else { on_20 },
196            on: on_component,
197            disabled: base_50,
198            on_disabled: on_65,
199            border,
200            disabled_border,
201        }
202    }
203}