1use deckcraft_color::{ColorScheme, ColorTransform, Rgba, SchemeSlot};
4use serde::{Deserialize, Serialize};
5
6use crate::style::{ColorRef, Effects, Fill, Gradient, GradientShape, GradientStop, Line, Shadow};
7
8#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
9#[serde(default, rename_all = "camelCase")]
10pub struct FontSet {
11 pub latin: String,
12 pub ea: String,
13 pub cs: String,
14 #[serde(skip_serializing_if = "Vec::is_empty")]
16 pub scripts: Vec<ScriptFont>,
17}
18
19#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
21#[serde(default, rename_all = "camelCase")]
22pub struct ScriptFont {
23 pub script: String,
24 pub typeface: String,
25}
26
27impl FontSet {
28 pub fn script(&self, script: &str) -> Option<&str> {
30 self.scripts
31 .iter()
32 .find(|f| f.script.eq_ignore_ascii_case(script) && !f.typeface.is_empty())
33 .map(|f| f.typeface.as_str())
34 }
35}
36
37#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
38#[serde(default, rename_all = "camelCase")]
39pub struct FontScheme {
40 pub name: String,
41 pub major: FontSet,
43 pub minor: FontSet,
45}
46
47#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
48#[serde(default, rename_all = "camelCase")]
49pub struct FormatScheme {
50 pub name: String,
51 pub fills: Vec<Fill>,
53 pub lines: Vec<Line>,
54 pub effects: Vec<Effects>,
55 pub bg_fills: Vec<Fill>,
57}
58
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60#[serde(default, rename_all = "camelCase")]
61pub struct Theme {
62 pub name: String,
63 pub colors: ColorScheme,
64 pub fonts: FontScheme,
65 pub format: FormatScheme,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub raw_extra: Option<String>,
69}
70
71impl Default for Theme {
72 fn default() -> Self {
73 builtin_themes()
74 .into_iter()
75 .next()
76 .unwrap_or_else(|| Theme {
77 name: "DeckCraft".into(),
78 colors: ColorScheme {
79 name: "DeckCraft".into(),
80 colors: [Rgba::BLACK; 12],
81 },
82 fonts: FontScheme::default(),
83 format: FormatScheme::default(),
84 raw_extra: None,
85 })
86 }
87}
88
89impl Theme {
90 pub fn font(&self, name: &str) -> String {
92 match name {
93 "+mj-lt" => self.fonts.major.latin.clone(),
94 "+mn-lt" => self.fonts.minor.latin.clone(),
95 "+mj-ea" => self.fonts.major.ea.clone(),
96 "+mn-ea" => self.fonts.minor.ea.clone(),
97 "+mj-cs" => self.fonts.major.cs.clone(),
98 "+mn-cs" => self.fonts.minor.cs.clone(),
99 other => other.to_string(),
100 }
101 }
102}
103
104fn hex(s: &str) -> Rgba {
105 Rgba::from_hex(s).unwrap_or(Rgba::BLACK)
106}
107
108fn scheme(name: &str, c: [&str; 12]) -> ColorScheme {
109 ColorScheme {
110 name: name.into(),
111 colors: c.map(hex),
112 }
113}
114
115fn ph() -> ColorRef {
116 ColorRef::scheme(SchemeSlot::PhClr)
117}
118
119pub fn default_format() -> FormatScheme {
121 let grad = |a: i32, b: i32| {
122 Fill::Gradient(Gradient {
123 stops: vec![
124 GradientStop {
125 pos: 0.0,
126 color: ph()
127 .with(ColorTransform::LumMod(a))
128 .with(ColorTransform::SatMod(103000)),
129 },
130 GradientStop {
131 pos: 1.0,
132 color: ph().with(ColorTransform::LumMod(b)),
133 },
134 ],
135 shape: GradientShape::Linear {
136 angle: 90.0,
137 scaled: false,
138 },
139 rotate_with_shape: true,
140 })
141 };
142 FormatScheme {
143 name: "DeckCraft".into(),
144 fills: vec![Fill::solid(ph()), grad(110000, 92000), grad(105000, 80000)],
145 lines: vec![
146 Line::solid(ph(), 0.75),
147 Line::solid(ph(), 1.0),
148 Line::solid(ph(), 1.5),
149 ],
150 effects: vec![
151 Effects::default(),
152 Effects::default(),
153 Effects {
154 outer_shadow: Some(Shadow {
155 color: ColorRef::rgb(Rgba::BLACK).with(ColorTransform::Alpha(40000)),
156 blur: 6.0,
157 dist: 3.0,
158 dir: 90.0,
159 inner: false,
160 sx: 1.0,
161 sy: 1.0,
162 kx: 0.0,
163 ky: 0.0,
164 align: "t".into(),
165 rotate_with_shape: false,
166 }),
167 ..Default::default()
168 },
169 ],
170 bg_fills: vec![
171 Fill::solid(ph()),
172 Fill::solid(
173 ph().with(ColorTransform::Tint(95000))
174 .with(ColorTransform::SatMod(170000)),
175 ),
176 grad(102000, 85000),
177 ],
178 }
179}
180
181pub fn builtin_themes() -> Vec<Theme> {
183 let t = |name: &str, colors: ColorScheme, major: &str, minor: &str| Theme {
184 name: name.into(),
185 colors,
186 fonts: FontScheme {
187 name: name.into(),
188 major: FontSet {
189 latin: major.into(),
190 ..Default::default()
191 },
192 minor: FontSet {
193 latin: minor.into(),
194 ..Default::default()
195 },
196 },
197 format: default_format(),
198 raw_extra: None,
199 };
200 vec![
201 t(
202 "DeckCraft",
203 scheme(
204 "DeckCraft",
205 [
206 "000000", "FFFFFF", "1E2B3C", "EDEBE7", "2E6FD8", "F26B1D", "2EA36F", "8A5CD8",
207 "D83F6B", "E3B21C", "2E6FD8", "7A5BA6",
208 ],
209 ),
210 "Inter",
211 "Inter",
212 ),
213 t(
214 "Harbor",
215 scheme(
216 "Harbor",
217 [
218 "0B1F2A", "FFFFFF", "12384D", "E4EEF2", "1B7FA6", "36B3A8", "F2C14E", "F78154",
219 "5D576B", "8AA29E", "1B7FA6", "5D576B",
220 ],
221 ),
222 "Merriweather",
223 "Open Sans",
224 ),
225 t(
226 "Ember",
227 scheme(
228 "Ember",
229 [
230 "1A1A1A", "FFFCF7", "3D2B1F", "F6EDE3", "D9480F", "F59F00", "A61E4D", "5C940D",
231 "1971C2", "862E9C", "D9480F", "862E9C",
232 ],
233 ),
234 "Montserrat",
235 "Lato",
236 ),
237 t(
238 "Meadow",
239 scheme(
240 "Meadow",
241 [
242 "14281D", "FFFFFF", "2D4A37", "EEF5EE", "4C9A2A", "A4C639", "2F6F4E", "E9B44C",
243 "9B2915", "50A2A7", "2F6F4E", "9B2915",
244 ],
245 ),
246 "Merriweather",
247 "Open Sans",
248 ),
249 t(
250 "Nocturne",
251 scheme(
252 "Nocturne",
253 [
254 "FFFFFF", "121826", "F2F4F8", "1E2638", "7C5CFF", "00C2A8", "FF6B9A", "FFC53D",
255 "4DA3FF", "B2F35F", "7C5CFF", "FF6B9A",
256 ],
257 ),
258 "Inter",
259 "Inter",
260 ),
261 t(
262 "Paper",
263 scheme(
264 "Paper",
265 [
266 "262626", "FFFFFF", "404040", "F2F2F2", "595959", "8C8C8C", "C0504D", "4F81BD",
267 "9BBB59", "F79646", "4F81BD", "8064A2",
268 ],
269 ),
270 "Playfair Display",
271 "Lato",
272 ),
273 t(
274 "Coral Reef",
275 scheme(
276 "Coral Reef",
277 [
278 "0F2A33", "FFFFFF", "0E4D64", "FDF1EC", "FF6F59", "254441", "43AA8B", "B2B09B",
279 "EF3054", "3F88C5", "3F88C5", "EF3054",
280 ],
281 ),
282 "Poppins",
283 "Nunito Sans",
284 ),
285 t(
286 "Slate",
287 scheme(
288 "Slate",
289 [
290 "111827", "FFFFFF", "374151", "F3F4F6", "6366F1", "0EA5E9", "10B981", "F59E0B",
291 "EF4444", "8B5CF6", "6366F1", "8B5CF6",
292 ],
293 ),
294 "Roboto",
295 "Roboto",
296 ),
297 ]
298}
299
300pub fn builtin_color_schemes() -> Vec<ColorScheme> {
302 let mut v: Vec<ColorScheme> = builtin_themes().into_iter().map(|t| t.colors).collect();
303 v.push(scheme(
304 "Grayscale",
305 [
306 "000000", "FFFFFF", "000000", "F8F8F8", "DDDDDD", "B2B2B2", "969696", "808080",
307 "5F5F5F", "4D4D4D", "5F5F5F", "919191",
308 ],
309 ));
310 v.push(scheme(
311 "Ocean",
312 [
313 "000000", "FFFFFF", "13293D", "E8F1F2", "006494", "247BA0", "1B98E0", "6CC1E8",
314 "13293D", "0B3954", "1B98E0", "6CC1E8",
315 ],
316 ));
317 v.push(scheme(
318 "Sunset",
319 [
320 "2B2118", "FFFFFF", "6F1A07", "FFF3E6", "FF9F1C", "FFBF69", "CB997E", "E76F51",
321 "F4A261", "2A9D8F", "E76F51", "2A9D8F",
322 ],
323 ));
324 v.push(scheme(
325 "Forest",
326 [
327 "1B1B1B", "FFFFFF", "283618", "FEFAE0", "606C38", "DDA15E", "BC6C25", "283618",
328 "8A9A5B", "4F772D", "606C38", "BC6C25",
329 ],
330 ));
331 v.push(scheme(
332 "Berry",
333 [
334 "1F0A1D", "FFFFFF", "4A1942", "F7E8F3", "893168", "C6426E", "EB6A8F", "4A1942",
335 "7D7ABC", "56A3A6", "893168", "56A3A6",
336 ],
337 ));
338 v
339}
340
341pub fn builtin_font_schemes() -> Vec<FontScheme> {
343 let f = |name: &str, major: &str, minor: &str| FontScheme {
344 name: name.into(),
345 major: FontSet {
346 latin: major.into(),
347 ..Default::default()
348 },
349 minor: FontSet {
350 latin: minor.into(),
351 ..Default::default()
352 },
353 };
354 vec![
355 f("Inter", "Inter", "Inter"),
356 f("Classic", "Merriweather", "Open Sans"),
357 f("Geometric", "Montserrat", "Lato"),
358 f("Editorial", "Playfair Display", "Lato"),
359 f("Humanist", "Merriweather", "Open Sans"),
360 f("Friendly", "Poppins", "Nunito Sans"),
361 f("Neutral", "Roboto", "Roboto"),
362 f("Office Compatible", "Carlito", "Carlito"),
363 f("Times / Arial", "Liberation Serif", "Liberation Sans"),
364 ]
365}