cosmic_theme/model/
derivation.rs

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