1#[derive(Debug, Clone, Copy)]
6pub struct ColorRGBA {
7 pub r: u8,
8 pub g: u8,
9 pub b: u8,
10 pub a: u8,
11}
12
13impl ColorRGBA {
14 pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
15 Self { r, g, b, a }
16 }
17
18 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
19 Self { r, g, b, a: 255 }
20 }
21
22 pub const fn transparent() -> Self {
23 Self { r: 0, g: 0, b: 0, a: 0 }
24 }
25}
26
27#[derive(Debug, Clone)]
29pub struct Theme {
30 pub name: &'static str,
31 pub bg_color: ColorRGBA,
33 pub panel_bg: ColorRGBA,
34 pub panel_border: ColorRGBA,
35 pub text_color: ColorRGBA,
37 pub text_dim: ColorRGBA,
38 pub text_highlight: ColorRGBA,
39 pub button_bg: ColorRGBA,
41 pub button_hover: ColorRGBA,
42 pub button_pressed: ColorRGBA,
43 pub button_border: ColorRGBA,
44 pub health_bar_bg: ColorRGBA,
46 pub health_bar_fill: ColorRGBA,
47 pub health_bar_border: ColorRGBA,
48 pub mana_bar_bg: ColorRGBA,
49 pub mana_bar_fill: ColorRGBA,
50 pub xp_bar_bg: ColorRGBA,
51 pub xp_bar_fill: ColorRGBA,
52 pub slot_bg: ColorRGBA,
54 pub slot_hover: ColorRGBA,
55 pub slot_border: ColorRGBA,
56 pub dialog_bg: ColorRGBA,
58 pub dialog_border: ColorRGBA,
59 pub menu_bg: ColorRGBA,
61 pub menu_item_bg: ColorRGBA,
62 pub menu_item_hover: ColorRGBA,
63 pub menu_item_selected: ColorRGBA,
64 pub font_size: u32,
66 pub font_size_small: u32,
67 pub font_size_title: u32,
68 pub padding: f32,
70 pub spacing: f32,
71 pub border_width: f32,
72}
73
74impl Theme {
75 pub fn dark() -> Self {
77 Self {
78 name: "Dark",
79 bg_color: ColorRGBA::rgb(18, 18, 24),
80 panel_bg: ColorRGBA::rgb(28, 28, 36),
81 panel_border: ColorRGBA::rgb(60, 60, 80),
82 text_color: ColorRGBA::rgb(230, 230, 240),
83 text_dim: ColorRGBA::rgb(140, 140, 160),
84 text_highlight: ColorRGBA::rgb(255, 255, 100),
85 button_bg: ColorRGBA::rgb(50, 50, 70),
86 button_hover: ColorRGBA::rgb(70, 70, 100),
87 button_pressed: ColorRGBA::rgb(90, 90, 130),
88 button_border: ColorRGBA::rgb(100, 100, 130),
89 health_bar_bg: ColorRGBA::rgb(40, 20, 20),
90 health_bar_fill: ColorRGBA::rgb(220, 50, 50),
91 health_bar_border: ColorRGBA::rgb(180, 40, 40),
92 mana_bar_bg: ColorRGBA::rgb(20, 20, 50),
93 mana_bar_fill: ColorRGBA::rgb(50, 100, 220),
94 xp_bar_bg: ColorRGBA::rgb(40, 40, 20),
95 xp_bar_fill: ColorRGBA::rgb(220, 200, 50),
96 slot_bg: ColorRGBA::rgb(35, 35, 45),
97 slot_hover: ColorRGBA::rgb(50, 50, 65),
98 slot_border: ColorRGBA::rgb(70, 70, 90),
99 dialog_bg: ColorRGBA::rgb(25, 25, 35),
100 dialog_border: ColorRGBA::rgb(80, 80, 100),
101 menu_bg: ColorRGBA::rgb(22, 22, 30),
102 menu_item_bg: ColorRGBA::rgb(30, 30, 42),
103 menu_item_hover: ColorRGBA::rgb(50, 50, 70),
104 menu_item_selected: ColorRGBA::rgb(70, 70, 100),
105 font_size: 16,
106 font_size_small: 12,
107 font_size_title: 20,
108 padding: 8.0,
109 spacing: 4.0,
110 border_width: 2.0,
111 }
112 }
113
114 pub fn light() -> Self {
116 Self {
117 name: "Light",
118 bg_color: ColorRGBA::rgb(240, 240, 245),
119 panel_bg: ColorRGBA::rgb(255, 255, 255),
120 panel_border: ColorRGBA::rgb(200, 200, 210),
121 text_color: ColorRGBA::rgb(30, 30, 40),
122 text_dim: ColorRGBA::rgb(120, 120, 140),
123 text_highlight: ColorRGBA::rgb(200, 160, 0),
124 button_bg: ColorRGBA::rgb(220, 220, 230),
125 button_hover: ColorRGBA::rgb(200, 200, 215),
126 button_pressed: ColorRGBA::rgb(180, 180, 200),
127 button_border: ColorRGBA::rgb(170, 170, 185),
128 health_bar_bg: ColorRGBA::rgb(230, 210, 210),
129 health_bar_fill: ColorRGBA::rgb(220, 60, 60),
130 health_bar_border: ColorRGBA::rgb(190, 50, 50),
131 mana_bar_bg: ColorRGBA::rgb(210, 210, 240),
132 mana_bar_fill: ColorRGBA::rgb(60, 110, 220),
133 xp_bar_bg: ColorRGBA::rgb(240, 240, 210),
134 xp_bar_fill: ColorRGBA::rgb(220, 200, 60),
135 slot_bg: ColorRGBA::rgb(235, 235, 240),
136 slot_hover: ColorRGBA::rgb(220, 220, 230),
137 slot_border: ColorRGBA::rgb(200, 200, 210),
138 dialog_bg: ColorRGBA::rgb(250, 250, 255),
139 dialog_border: ColorRGBA::rgb(190, 190, 200),
140 menu_bg: ColorRGBA::rgb(245, 245, 250),
141 menu_item_bg: ColorRGBA::rgb(240, 240, 248),
142 menu_item_hover: ColorRGBA::rgb(220, 220, 235),
143 menu_item_selected: ColorRGBA::rgb(200, 200, 220),
144 font_size: 16,
145 font_size_small: 12,
146 font_size_title: 20,
147 padding: 8.0,
148 spacing: 4.0,
149 border_width: 1.0,
150 }
151 }
152
153 pub fn retro() -> Self {
155 Self {
156 name: "Retro",
157 bg_color: ColorRGBA::rgb(0, 0, 0),
158 panel_bg: ColorRGBA::rgb(32, 32, 32),
159 panel_border: ColorRGBA::rgb(255, 255, 255),
160 text_color: ColorRGBA::rgb(0, 255, 0),
161 text_dim: ColorRGBA::rgb(0, 180, 0),
162 text_highlight: ColorRGBA::rgb(255, 255, 0),
163 button_bg: ColorRGBA::rgb(64, 64, 64),
164 button_hover: ColorRGBA::rgb(96, 96, 96),
165 button_pressed: ColorRGBA::rgb(128, 128, 128),
166 button_border: ColorRGBA::rgb(255, 255, 255),
167 health_bar_bg: ColorRGBA::rgb(32, 0, 0),
168 health_bar_fill: ColorRGBA::rgb(255, 0, 0),
169 health_bar_border: ColorRGBA::rgb(255, 255, 255),
170 mana_bar_bg: ColorRGBA::rgb(0, 0, 32),
171 mana_bar_fill: ColorRGBA::rgb(0, 100, 255),
172 xp_bar_bg: ColorRGBA::rgb(32, 32, 0),
173 xp_bar_fill: ColorRGBA::rgb(255, 255, 0),
174 slot_bg: ColorRGBA::rgb(48, 48, 48),
175 slot_hover: ColorRGBA::rgb(80, 80, 80),
176 slot_border: ColorRGBA::rgb(255, 255, 255),
177 dialog_bg: ColorRGBA::rgb(0, 0, 64),
178 dialog_border: ColorRGBA::rgb(255, 255, 255),
179 menu_bg: ColorRGBA::rgb(0, 0, 0),
180 menu_item_bg: ColorRGBA::rgb(0, 0, 0),
181 menu_item_hover: ColorRGBA::rgb(0, 64, 0),
182 menu_item_selected: ColorRGBA::rgb(0, 128, 0),
183 font_size: 14,
184 font_size_small: 10,
185 font_size_title: 18,
186 padding: 6.0,
187 spacing: 3.0,
188 border_width: 2.0,
189 }
190 }
191
192 pub fn neon() -> Self {
194 Self {
195 name: "Neon",
196 bg_color: ColorRGBA::rgb(5, 5, 15),
197 panel_bg: ColorRGBA::rgb(10, 10, 30),
198 panel_border: ColorRGBA::rgb(0, 255, 255),
199 text_color: ColorRGBA::rgb(255, 255, 255),
200 text_dim: ColorRGBA::rgb(100, 200, 255),
201 text_highlight: ColorRGBA::rgb(255, 0, 255),
202 button_bg: ColorRGBA::rgb(20, 20, 50),
203 button_hover: ColorRGBA::rgb(40, 40, 100),
204 button_pressed: ColorRGBA::rgb(60, 60, 150),
205 button_border: ColorRGBA::rgb(0, 255, 255),
206 health_bar_bg: ColorRGBA::rgb(30, 5, 5),
207 health_bar_fill: ColorRGBA::rgb(255, 50, 50),
208 health_bar_border: ColorRGBA::rgb(255, 100, 100),
209 mana_bar_bg: ColorRGBA::rgb(5, 5, 30),
210 mana_bar_fill: ColorRGBA::rgb(100, 150, 255),
211 xp_bar_bg: ColorRGBA::rgb(30, 30, 5),
212 xp_bar_fill: ColorRGBA::rgb(255, 255, 100),
213 slot_bg: ColorRGBA::rgb(15, 15, 35),
214 slot_hover: ColorRGBA::rgb(30, 30, 70),
215 slot_border: ColorRGBA::rgb(0, 255, 255),
216 dialog_bg: ColorRGBA::rgb(8, 8, 25),
217 dialog_border: ColorRGBA::rgb(255, 0, 255),
218 menu_bg: ColorRGBA::rgb(5, 5, 20),
219 menu_item_bg: ColorRGBA::rgb(10, 10, 30),
220 menu_item_hover: ColorRGBA::rgb(30, 30, 70),
221 menu_item_selected: ColorRGBA::rgb(50, 50, 120),
222 font_size: 16,
223 font_size_small: 12,
224 font_size_title: 22,
225 padding: 10.0,
226 spacing: 6.0,
227 border_width: 1.0,
228 }
229 }
230
231 pub fn minimal() -> Self {
233 Self {
234 name: "Minimal",
235 bg_color: ColorRGBA::transparent(),
236 panel_bg: ColorRGBA::rgb(20, 20, 25),
237 panel_border: ColorRGBA::transparent(),
238 text_color: ColorRGBA::rgb(240, 240, 245),
239 text_dim: ColorRGBA::rgb(120, 120, 130),
240 text_highlight: ColorRGBA::rgb(255, 255, 255),
241 button_bg: ColorRGBA::transparent(),
242 button_hover: ColorRGBA::rgb(40, 40, 50),
243 button_pressed: ColorRGBA::rgb(60, 60, 70),
244 button_border: ColorRGBA::transparent(),
245 health_bar_bg: ColorRGBA::rgb(40, 20, 20),
246 health_bar_fill: ColorRGBA::rgb(230, 60, 60),
247 health_bar_border: ColorRGBA::transparent(),
248 mana_bar_bg: ColorRGBA::rgb(20, 20, 50),
249 mana_bar_fill: ColorRGBA::rgb(60, 120, 230),
250 xp_bar_bg: ColorRGBA::rgb(40, 40, 20),
251 xp_bar_fill: ColorRGBA::rgb(230, 210, 60),
252 slot_bg: ColorRGBA::rgb(30, 30, 38),
253 slot_hover: ColorRGBA::rgb(45, 45, 55),
254 slot_border: ColorRGBA::transparent(),
255 dialog_bg: ColorRGBA::rgb(15, 15, 22),
256 dialog_border: ColorRGBA::transparent(),
257 menu_bg: ColorRGBA::transparent(),
258 menu_item_bg: ColorRGBA::transparent(),
259 menu_item_hover: ColorRGBA::rgb(30, 30, 40),
260 menu_item_selected: ColorRGBA::rgb(50, 50, 60),
261 font_size: 15,
262 font_size_small: 11,
263 font_size_title: 19,
264 padding: 6.0,
265 spacing: 3.0,
266 border_width: 0.0,
267 }
268 }
269
270 pub fn all() -> &'static [fn() -> Self] {
272 &[Self::dark, Self::light, Self::retro, Self::neon, Self::minimal]
273 }
274
275 pub fn by_name(name: &str) -> Self {
277 match name.to_lowercase().as_str() {
278 "dark" | "oscuro" => Self::dark(),
279 "light" | "claro" => Self::light(),
280 "retro" | "pixel" | "8bit" => Self::retro(),
281 "neon" | "cyber" | "cyberpunk" => Self::neon(),
282 "minimal" | "minimalist" | "min" => Self::minimal(),
283 _ => Self::dark(),
284 }
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn test_theme_dark() {
294 let theme = Theme::dark();
295 assert_eq!(theme.name, "Dark");
296 assert_eq!(theme.bg_color.r, 18);
297 }
298
299 #[test]
300 fn test_theme_light() {
301 let theme = Theme::light();
302 assert_eq!(theme.name, "Light");
303 assert_eq!(theme.bg_color.r, 240);
304 }
305
306 #[test]
307 fn test_theme_retro() {
308 let theme = Theme::retro();
309 assert_eq!(theme.name, "Retro");
310 assert_eq!(theme.text_color.g, 255);
311 }
312
313 #[test]
314 fn test_theme_neon() {
315 let theme = Theme::neon();
316 assert_eq!(theme.name, "Neon");
317 assert_eq!(theme.panel_border.g, 255);
318 }
319
320 #[test]
321 fn test_theme_minimal() {
322 let theme = Theme::minimal();
323 assert_eq!(theme.name, "Minimal");
324 assert_eq!(theme.border_width, 0.0);
325 }
326
327 #[test]
328 fn test_theme_by_name() {
329 let dark = Theme::by_name("dark");
330 assert_eq!(dark.name, "Dark");
331
332 let retro = Theme::by_name("retro");
333 assert_eq!(retro.name, "Retro");
334
335 let neon = Theme::by_name("cyber");
336 assert_eq!(neon.name, "Neon");
337
338 let default = Theme::by_name("unknown");
339 assert_eq!(default.name, "Dark");
340 }
341
342 #[test]
343 fn test_theme_all() {
344 let themes = Theme::all();
345 assert_eq!(themes.len(), 5);
346 }
347}