1use cleansys_core::AppTheme;
8use iced::{Color, Theme};
9
10#[derive(Debug, Clone, Copy)]
17pub struct ThemeColors {
18 pub accent: Color,
19 pub text_primary: Color,
20 pub text_secondary: Color,
21 pub muted: Color,
22 pub bg: Color,
23 pub surface: Color,
24 pub surface_highlight: Color,
25 pub header_bg: Color,
26 pub border: Color,
27 pub selection: Color,
28 pub green: Color,
29 pub red: Color,
30 pub yellow: Color,
31}
32
33fn clamp(v: f32) -> f32 {
34 v.clamp(0.0, 1.0)
35}
36
37fn shift(base: Color, delta: f32) -> Color {
39 Color {
40 r: clamp(base.r + delta),
41 g: clamp(base.g + delta),
42 b: clamp(base.b + delta),
43 a: base.a,
44 }
45}
46
47fn rgb_to_iced(rgb: cleansys_core::Rgb) -> Color {
49 Color::from_rgb8(rgb.r, rgb.g, rgb.b)
50}
51
52impl ThemeColors {
53 pub fn from_core(t: &AppTheme) -> Self {
55 let bg = rgb_to_iced(t.background);
56 let surface = rgb_to_iced(t.surface);
57
58 let sign: f32 = if t.is_dark { 1.0 } else { -1.0 };
59 let surface_highlight = shift(surface, sign * 0.04);
60 let header_bg = shift(bg, sign * 0.02);
61
62 Self {
63 accent: rgb_to_iced(t.accent),
64 text_primary: rgb_to_iced(t.text_primary),
65 text_secondary: rgb_to_iced(t.text_secondary),
66 muted: rgb_to_iced(t.text_muted),
67 bg,
68 surface,
69 surface_highlight,
70 header_bg,
71 border: rgb_to_iced(t.border),
72 selection: rgb_to_iced(t.selection),
73 green: rgb_to_iced(t.success),
74 red: rgb_to_iced(t.error),
75 yellow: rgb_to_iced(t.warning),
76 }
77 }
78}
79
80pub fn iced_theme_for(index: usize) -> Theme {
85 let core = cleansys_core::theme_by_index(index);
86 let name = cleansys_core::THEME_NAMES
87 .get(index)
88 .copied()
89 .unwrap_or("Default")
90 .to_string();
91
92 let palette = iced::theme::Palette {
93 background: rgb_to_iced(core.background),
94 text: rgb_to_iced(core.text_primary),
95 primary: rgb_to_iced(core.accent),
96 success: rgb_to_iced(core.success),
97 warning: rgb_to_iced(core.warning),
98 danger: rgb_to_iced(core.error),
99 };
100
101 Theme::custom(name, palette)
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn from_core_produces_valid_colors() {
110 for i in 0..cleansys_core::THEME_COUNT {
111 let core = cleansys_core::theme_by_index(i);
112 let colors = ThemeColors::from_core(&core);
113 assert!(colors.bg.a > 0.0);
115 assert!(colors.text_primary.a > 0.0);
116 }
117 }
118
119 #[test]
120 fn iced_theme_for_all_indices_does_not_panic() {
121 for i in 0..cleansys_core::THEME_COUNT {
122 let _ = iced_theme_for(i);
123 }
124 }
125
126 #[test]
127 fn iced_theme_for_out_of_range_falls_back_to_default_name() {
128 let theme = iced_theme_for(9999);
131 assert_eq!(format!("{theme}"), "Default");
132 }
133}