1use palette::Srgba;
2use serde::{Deserialize, Serialize};
3
4use crate::composite::over;
5
6#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
8#[must_use]
9pub struct Container {
10 pub base: Srgba,
12 pub component: Component,
14 pub divider: Srgba,
16 pub on: Srgba,
18 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#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
47#[must_use]
48pub struct Component {
49 pub base: Srgba,
51 pub hover: Srgba,
53 pub pressed: Srgba,
55 pub selected: Srgba,
57 pub selected_text: Srgba,
59 pub focus: Srgba,
61 pub divider: Srgba,
63 pub on: Srgba,
65 pub disabled: Srgba,
69 pub on_disabled: Srgba,
71 pub border: Srgba,
73 pub disabled_border: Srgba,
75}
76
77#[allow(clippy::must_use_candidate)]
78#[allow(clippy::doc_markdown)]
79impl Component {
80 #[inline]
81 pub fn hover_state_color(&self) -> Srgba {
83 self.hover
84 }
85
86 #[inline]
87 pub fn pressed_state_color(&self) -> Srgba {
89 self.pressed
90 }
91
92 #[inline]
93 pub fn selected_state_color(&self) -> Srgba {
95 self.selected
96 }
97
98 #[inline]
99 pub fn selected_state_text_color(&self) -> Srgba {
101 self.selected_text
102 }
103
104 #[inline]
105 pub fn focus_color(&self) -> Srgba {
107 self.focus
108 }
109
110 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 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 #[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}