1use palette::{Srgba, WithAlpha};
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 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#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
45#[must_use]
46pub struct Component {
47 pub base: Srgba,
49 pub hover: Srgba,
51 pub pressed: Srgba,
53 pub selected: Srgba,
55 pub selected_text: Srgba,
57 pub focus: Srgba,
59 pub divider: Srgba,
61 pub on: Srgba,
63 pub disabled: Srgba,
67 pub on_disabled: Srgba,
69 pub border: Srgba,
71 pub disabled_border: Srgba,
73}
74
75#[allow(clippy::must_use_candidate)]
76#[allow(clippy::doc_markdown)]
77impl Component {
78 #[inline]
79 pub fn hover_state_color(&self) -> Srgba {
81 self.hover
82 }
83
84 #[inline]
85 pub fn pressed_state_color(&self) -> Srgba {
87 self.pressed
88 }
89
90 #[inline]
91 pub fn selected_state_color(&self) -> Srgba {
93 self.selected
94 }
95
96 #[inline]
97 pub fn selected_state_text_color(&self) -> Srgba {
99 self.selected_text
100 }
101
102 #[inline]
103 pub fn focus_color(&self) -> Srgba {
105 self.focus
106 }
107
108 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 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 #[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}