1use crate::overlay::OverlayManager;
11use crate::themes::{
12 mclaren_650gts_gt3, reverie, shade_sanctuary, tokyo_night, tokyo_night_light, wasp, wasp_light,
13};
14use crate::utils::*;
15use crate::visuals::*;
16use egui::{Color32, Context, Frame, Id, Style, Vec2};
17
18#[cfg(feature = "elegance")]
19use elegance::{Palette, Theme as EleganceTheme, Typography as EleganceTypography};
20
21#[cfg(feature = "elegance")]
23pub fn elegance_theme_key() -> Id {
24 Id::new("elegance::theme")
25}
26
27#[cfg(feature = "elegance")]
29#[derive(Clone, Copy, PartialEq, Eq)]
30pub struct EleganceThemeKey {
31 dark: bool,
32 bg: Color32,
33 widget_bg: Color32,
34 border: Color32,
35 text: Color32,
36 text_muted: Color32,
37 accent: Color32,
38 info: Color32,
39 success: Color32,
40 error: Color32,
41 warning: Color32,
42}
43
44#[cfg(feature = "elegance")]
45impl EleganceThemeKey {
46 fn from_theme(theme: &Theme) -> Self {
47 let c = &theme.colors;
48 Self {
49 dark: theme.dark,
50 bg: c.bg,
51 widget_bg: c.widget_bg,
52 border: c.border,
53 text: c.text,
54 text_muted: c.text_muted,
55 accent: c.accent,
56 info: c.info,
57 success: c.success,
58 error: c.error,
59 warning: c.warning,
60 }
61 }
62}
63
64#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub enum ThemeKind {
68 TokyoNight,
70 TokyoNightLight,
72 McLaren650Gts,
74 Reverie,
76 ShadeSanctuary,
78 Wasp,
80 WaspLight,
82}
83
84impl ThemeKind {
85 pub fn to_str(&self) -> &str {
87 match self {
88 ThemeKind::TokyoNight => "Tokyo Night",
89 ThemeKind::TokyoNightLight => "Tokyo Night Light",
90 ThemeKind::McLaren650Gts => "McLaren 650GTS",
91 ThemeKind::Reverie => "Reverie",
92 ThemeKind::ShadeSanctuary => "Shade Sanctuary",
93 ThemeKind::Wasp => "Wasp",
94 ThemeKind::WaspLight => "Wasp Light",
95 }
96 }
97
98 pub fn to_vec() -> Vec<Self> {
100 vec![
101 Self::TokyoNight,
102 Self::TokyoNightLight,
103 Self::McLaren650Gts,
104 Self::Reverie,
105 Self::ShadeSanctuary,
106 Self::Wasp,
107 Self::WaspLight,
108 ]
109 }
110}
111
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126#[derive(Clone)]
127pub struct Theme {
128 pub dark: bool,
130 #[cfg_attr(feature = "serde", serde(skip))]
132 pub overlay_manager: OverlayManager,
133
134 pub image_tint_recommended: bool,
139
140 pub kind: ThemeKind,
142 pub colors: ThemeColors,
144 pub visuals: ThemeVisuals,
146 pub typography: Typography,
148
149 pub window_frame: Frame,
151 pub frame1: Frame,
153 pub frame2: Frame,
155
156 pub corner_radius: u8,
158
159 pub inner_margin: i8,
161
162 pub outer_margin: i8,
164
165 pub button_padding: Vec2,
169
170 #[cfg(feature = "elegance")]
171 #[cfg_attr(feature = "serde", serde(skip))]
172 pub elegance_key: Option<EleganceThemeKey>,
174}
175
176impl PartialEq for Theme {
177 fn eq(&self, other: &Self) -> bool {
178 self.dark == other.dark
179 && self.kind == other.kind
180 && self.colors == other.colors
181 && self.typography == other.typography
182 && self.window_frame == other.window_frame
183 && self.frame1 == other.frame1
184 && self.frame2 == other.frame2
185 && self.visuals == other.visuals
186 }
187}
188
189impl Eq for Theme {}
190
191impl Theme {
192 pub fn new(kind: ThemeKind) -> Self {
194 let theme = match kind {
195 ThemeKind::TokyoNight => tokyo_night::theme(),
196 ThemeKind::TokyoNightLight => tokyo_night_light::theme(),
197 ThemeKind::McLaren650Gts => mclaren_650gts_gt3::theme(),
198 ThemeKind::Reverie => reverie::theme(),
199 ThemeKind::ShadeSanctuary => shade_sanctuary::theme(),
200 ThemeKind::Wasp => wasp::theme(),
201 ThemeKind::WaspLight => wasp_light::theme(),
202 };
203
204 theme
205 }
206
207 pub fn style(&self) -> Style {
209 match self.kind {
210 ThemeKind::TokyoNight => tokyo_night::style(),
211 ThemeKind::TokyoNightLight => tokyo_night_light::style(),
212 ThemeKind::McLaren650Gts => mclaren_650gts_gt3::style(),
213 ThemeKind::Reverie => reverie::style(),
214 ThemeKind::ShadeSanctuary => shade_sanctuary::style(),
215 ThemeKind::Wasp => wasp::style(),
216 ThemeKind::WaspLight => wasp_light::style(),
217 }
218 }
219
220 pub fn frame1_visuals(&self) -> FrameVisuals {
222 self.visuals.frame1_visuals
223 }
224
225 pub fn frame2_visuals(&self) -> FrameVisuals {
227 self.visuals.frame2_visuals
228 }
229
230 pub fn button_visuals(&self) -> ButtonVisuals {
232 self.visuals.button_visuals
233 }
234
235 pub fn label_visuals(&self) -> LabelVisuals {
237 self.visuals.label_visuals
238 }
239
240 pub fn combo_box_visuals(&self) -> ComboBoxVisuals {
242 self.visuals.combo_box_visuals
243 }
244
245 pub fn text_edit_visuals(&self) -> TextEditVisuals {
247 self.visuals.text_edit_visuals
248 }
249
250 fn storage_id() -> Id {
251 Id::new("elements::theme")
252 }
253
254 fn button_visuals_id() -> Id {
255 Id::new("elements::button_visuals")
256 }
257
258 fn label_visuals_id() -> Id {
259 Id::new("elements::label_visuals")
260 }
261
262 fn combo_box_visuals_id() -> Id {
263 Id::new("elements::combo_box_visuals")
264 }
265
266 fn text_edit_visuals_id() -> Id {
267 Id::new("elements::text_edit_visuals")
268 }
269
270 pub fn button_visuals_from_ctx(ctx: &Context) -> Option<ButtonVisuals> {
274 ctx.data(|d| d.get_temp(Self::button_visuals_id()))
275 }
276
277 pub fn label_visuals_from_ctx(ctx: &Context) -> Option<LabelVisuals> {
279 ctx.data(|d| d.get_temp(Self::label_visuals_id()))
280 }
281
282 pub fn combo_box_visuals_from_ctx(ctx: &Context) -> Option<ComboBoxVisuals> {
284 ctx.data(|d| d.get_temp(Self::combo_box_visuals_id()))
285 }
286
287 pub fn text_edit_visuals_from_ctx(ctx: &Context) -> Option<TextEditVisuals> {
289 ctx.data(|d| d.get_temp(Self::text_edit_visuals_id()))
290 }
291
292 pub fn install(&mut self, ctx: &Context) {
296 #[cfg(feature = "elegance")]
297 self.install_elegance_theme(ctx);
298
299 let button_visuals = self.button_visuals();
300 let label_visuals = self.label_visuals();
301 let combo_box_visuals = self.combo_box_visuals();
302 let text_edit_visuals = self.text_edit_visuals();
303 let style = self.style();
304 let theme = self.clone();
305
306 ctx.set_global_style(style);
307 ctx.data_mut(|d| d.insert_temp(Self::storage_id(), theme));
308
309 ctx.data_mut(|d| d.insert_temp(Self::button_visuals_id(), button_visuals));
310 ctx.data_mut(|d| d.insert_temp(Self::label_visuals_id(), label_visuals));
311 ctx.data_mut(|d| d.insert_temp(Self::combo_box_visuals_id(), combo_box_visuals));
312 ctx.data_mut(|d| d.insert_temp(Self::text_edit_visuals_id(), text_edit_visuals));
313 }
314
315 #[cfg(feature = "elegance")]
316 pub fn install_elegance_theme(&mut self, ctx: &Context) {
320 let key = EleganceThemeKey::from_theme(&self);
321 if self.elegance_key == Some(key) {
322 return;
323 }
324
325 let c = &self.colors;
326 let mut pal = if key.dark {
327 Palette::charcoal()
328 } else {
329 Palette::frost()
330 };
331
332 pal.is_dark = key.dark;
333 pal.bg = c.bg;
334 pal.card = c.widget_bg;
335 pal.input_bg = c.widget_bg;
336 pal.border = c.border;
337 pal.text = c.text;
338 pal.text_muted = c.text_muted;
339 pal.text_faint = c.text_muted;
340 pal.focus = c.accent;
341 pal.blue = c.info;
342 pal.green = c.success;
343 pal.green_hover = c.success;
344 pal.red = c.error;
345 pal.red_hover = c.error;
346 pal.amber = c.warning;
347 pal.amber_hover = c.warning;
348 pal.purple = c.accent;
349 pal.purple_hover = c.accent;
350 pal.success = c.success;
351 pal.danger = c.error;
352 pal.warning = c.warning;
353
354 let elegance_typography = EleganceTypography {
355 body: self.typography.normal,
356 button: self.typography.normal,
357 label: self.typography.normal,
358 small: self.typography.small,
359 heading: self.typography.heading,
360 monospace: self.typography.small,
361 };
362
363 let elegance_theme = EleganceTheme {
364 palette: pal,
365 control_radius: self.corner_radius as f32,
366 card_radius: self.corner_radius as f32,
367 card_padding: self.frame1.inner_margin.top as f32,
368 control_padding_y: self.button_padding.y as f32,
369 control_padding_x: self.button_padding.x as f32,
370 typography: elegance_typography,
371 };
372
373 ctx.data_mut(|d| d.insert_temp(elegance_theme_key(), elegance_theme));
374 self.elegance_key = Some(key);
375 }
376
377 pub fn current(ctx: &Context) -> Theme {
380 ctx.data(|d| {
381 d.get_temp::<Theme>(Self::storage_id())
382 .unwrap_or_else(|| Theme::new(ThemeKind::TokyoNight))
383 })
384 }
385
386 pub fn remap_derived_frames(&mut self, old: &ThemeColors) {
392 let new = self.colors;
393 if !frame_palette_changed(old, &new) {
394 return;
395 }
396
397 remap_frame(
398 &mut self.window_frame,
399 old.title_bar,
400 new.title_bar,
401 old.border,
402 new.border,
403 );
404 remap_frame(
405 &mut self.frame1,
406 old.widget_bg,
407 new.widget_bg,
408 old.border,
409 new.border,
410 );
411 remap_frame(
412 &mut self.frame2,
413 old.bg,
414 new.bg,
415 old.border,
416 new.border,
417 );
418 remap_frame_visuals(
419 &mut self.visuals.frame1_visuals,
420 old.hover,
421 new.hover,
422 old.widget_bg,
423 new.widget_bg,
424 old.highlight,
425 new.highlight,
426 );
427 remap_frame_visuals(
428 &mut self.visuals.frame2_visuals,
429 old.hover,
430 new.hover,
431 old.bg,
432 new.bg,
433 old.highlight,
434 new.highlight,
435 );
436 }
437}
438
439#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
441#[derive(Copy, Clone, Debug, PartialEq, Eq)]
442pub struct ThemeVisuals {
443 pub button_visuals: ButtonVisuals,
445 pub label_visuals: LabelVisuals,
447 pub combo_box_visuals: ComboBoxVisuals,
449 pub text_edit_visuals: TextEditVisuals,
451 pub frame1_visuals: FrameVisuals,
453 pub frame2_visuals: FrameVisuals,
455}
456
457#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
459#[derive(Copy, Clone, Debug, PartialEq, Eq)]
460pub struct ThemeColors {
461 pub title_bar: Color32,
463
464 pub bg: Color32,
466
467 pub widget_bg: Color32,
471
472 pub hover: Color32,
474
475 pub text: Color32,
477
478 pub text_muted: Color32,
482
483 pub highlight: Color32,
485
486 pub border: Color32,
488
489 pub accent: Color32,
491
492 pub error: Color32,
496
497 pub warning: Color32,
499
500 pub success: Color32,
504
505 pub info: Color32,
509}
510
511#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
513#[derive(Clone, Default, Debug, PartialEq)]
514pub struct Typography {
515 pub very_small: f32,
517 pub small: f32,
519 pub normal: f32,
521 pub large: f32,
523 pub very_large: f32,
525 pub heading: f32,
527}