1use crate::{
2 blend::harmonize,
3 color::Argb,
4 dynamic_color::{DynamicScheme, Variant},
5 palette::{CorePalette, Palette, TonalPalette},
6 scheme::Scheme,
7};
8#[cfg(not(feature = "std"))]
9use alloc::{string::String, vec::Vec};
10#[cfg(feature = "serde")]
11use serde::Serialize;
12#[cfg(feature = "std")]
13use std::{string::String, vec::Vec};
14
15#[derive(Debug)]
17#[cfg_attr(feature = "serde", derive(Serialize))]
18pub struct CustomColor {
19 pub value: Argb,
20 pub name: String,
21 pub blend: bool,
22}
23
24#[derive(Debug)]
26#[cfg_attr(feature = "serde", derive(Serialize))]
27pub struct ColorGroup {
28 pub color: Argb,
29 pub on_color: Argb,
30 pub color_container: Argb,
31 pub on_color_container: Argb,
32}
33
34#[derive(Debug)]
36#[cfg_attr(feature = "serde", derive(Serialize))]
37pub struct CustomColorGroup {
38 pub color: CustomColor,
39 pub value: Argb,
40 pub light: ColorGroup,
41 pub dark: ColorGroup,
42}
43
44impl CustomColorGroup {
45 fn new(source: Argb, color: CustomColor) -> Self {
49 let mut value = color.value;
50
51 if color.blend {
52 value = harmonize(value, source);
53 }
54
55 let palette = CorePalette::of(value);
56 let tones = palette.primary;
57
58 Self {
59 color,
60 value,
61 light: ColorGroup {
62 color: tones.tone(40),
63 on_color: tones.tone(100),
64 color_container: tones.tone(90),
65 on_color_container: tones.tone(10),
66 },
67 dark: ColorGroup {
68 color: tones.tone(80),
69 on_color: tones.tone(20),
70 color_container: tones.tone(30),
71 on_color_container: tones.tone(90),
72 },
73 }
74 }
75}
76
77#[derive(Debug)]
78#[cfg_attr(feature = "serde", derive(Serialize))]
79pub struct Schemes {
80 pub light: Scheme,
81 pub dark: Scheme,
82}
83
84#[derive(Debug)]
85#[cfg_attr(feature = "serde", derive(Serialize))]
86pub struct Palettes {
87 pub primary: TonalPalette,
88 pub secondary: TonalPalette,
89 pub tertiary: TonalPalette,
90 pub neutral: TonalPalette,
91 pub neutral_variant: TonalPalette,
92 pub error: TonalPalette,
93}
94
95pub struct ThemeBuilder {
96 source: Argb,
97 variant: Variant,
98 color_match: bool,
99 primary: Option<Argb>,
100 secondary: Option<Argb>,
101 tertiary: Option<Argb>,
102 error: Option<Argb>,
103 neutral: Option<Argb>,
104 neutral_variant: Option<Argb>,
105 custom_colors: Vec<CustomColor>,
106}
107
108impl ThemeBuilder {
109 #[must_use]
111 pub const fn with_source(source: Argb) -> Self {
112 Self {
113 source,
114 variant: Variant::TonalSpot,
115 color_match: false,
116 primary: None,
117 secondary: None,
118 tertiary: None,
119 error: None,
120 neutral: None,
121 neutral_variant: None,
122 custom_colors: Vec::new(),
123 }
124 }
125
126 #[must_use]
128 pub const fn variant(mut self, variant: Variant) -> Self {
129 self.variant = variant;
130
131 self
132 }
133
134 #[must_use]
136 pub const fn primary(mut self, color: Argb) -> Self {
137 self.primary = Some(color);
138
139 self
140 }
141
142 #[must_use]
144 pub const fn secondary(mut self, color: Argb) -> Self {
145 self.secondary = Some(color);
146
147 self
148 }
149
150 #[must_use]
152 pub const fn tertiary(mut self, color: Argb) -> Self {
153 self.tertiary = Some(color);
154
155 self
156 }
157
158 #[must_use]
160 pub const fn error(mut self, color: Argb) -> Self {
161 self.error = Some(color);
162
163 self
164 }
165
166 #[must_use]
168 pub const fn neutral(mut self, color: Argb) -> Self {
169 self.neutral = Some(color);
170
171 self
172 }
173
174 #[must_use]
176 pub const fn neutral_variant(mut self, color: Argb) -> Self {
177 self.neutral_variant = Some(color);
178
179 self
180 }
181
182 #[must_use]
186 pub fn custom_colors(mut self, custom_colors: Vec<CustomColor>) -> Self {
187 self.custom_colors = custom_colors;
188
189 self
190 }
191
192 #[must_use]
193 pub const fn color_match(mut self, enabled: bool) -> Self {
194 self.color_match = enabled;
195
196 self
197 }
198
199 #[must_use]
200 pub fn build(mut self) -> Theme {
201 let palette = CorePalette::of(self.source);
202
203 if self.color_match {
204 self.variant = Variant::Fidelity;
205 }
206
207 let mut light = DynamicScheme::by_variant(self.source, &self.variant, false, None);
208 let mut dark = DynamicScheme::by_variant(self.source, &self.variant, true, None);
209
210 if let Some(color) = self.primary {
211 let palette = TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Primary);
212
213 light.primary_palette = palette;
214 dark.primary_palette = palette;
215 }
216
217 if let Some(color) = self.secondary {
218 let palette =
219 TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Secondary);
220
221 light.secondary_palette = palette;
222 dark.secondary_palette = palette;
223 }
224
225 if let Some(color) = self.tertiary {
226 let palette =
227 TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Tertiary);
228
229 light.tertiary_palette = palette;
230 dark.tertiary_palette = palette;
231 }
232
233 if let Some(color) = self.error {
234 let palette = TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Error);
235
236 light.error_palette = palette;
237 dark.error_palette = palette;
238 }
239
240 if let Some(color) = self.neutral {
241 let palette = TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Neutral);
242
243 light.neutral_palette = palette;
244 dark.neutral_palette = palette;
245 }
246
247 if let Some(color) = self.neutral_variant {
248 let palette =
249 TonalPalette::by_variant(&color.into(), &self.variant, &Palette::NeutralVariant);
250
251 light.neutral_variant_palette = palette;
252 dark.neutral_variant_palette = palette;
253 }
254
255 Theme {
256 source: self.source,
257 schemes: Schemes {
258 light: light.into(),
259 dark: dark.into(),
260 },
261 palettes: Palettes {
262 primary: palette.primary,
263 secondary: palette.secondary,
264 tertiary: palette.tertiary,
265 neutral: palette.neutral,
266 neutral_variant: palette.neutral_variant,
267 error: palette.error,
268 },
269 custom_colors: self
270 .custom_colors
271 .into_iter()
272 .map(|color| CustomColorGroup::new(self.source, color))
273 .collect(),
274 }
275 }
276}
277
278#[derive(Debug)]
279#[cfg_attr(feature = "serde", derive(Serialize))]
280pub struct Theme {
281 pub source: Argb,
282 pub schemes: Schemes,
283 pub palettes: Palettes,
284 pub custom_colors: Vec<CustomColorGroup>,
285}