1use egui::{Color32, Context, Frame, Id, LayerId, Order, Rect, Style};
2use std::sync::{Arc, RwLock};
3
4const PANIC_MSG: &str = "Custom theme not supported, use Theme::from_custom() instead";
5
6pub mod editor;
7pub mod hsla;
8pub mod themes;
9pub mod utils;
10pub mod visuals;
11pub mod window;
12
13pub use editor::ThemeEditor;
14use themes::*;
15pub use visuals::*;
16
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[derive(Debug, Clone, PartialEq)]
19pub enum ThemeKind {
20 Dark,
21
22 TokyoNight,
23
24 Custom,
29}
30
31impl ThemeKind {
32 pub fn to_str(&self) -> &str {
33 match self {
34 ThemeKind::Dark => "Dark",
35 ThemeKind::TokyoNight => "Tokyo Night",
36 ThemeKind::Custom => "Custom",
38 }
39 }
40
41 pub fn to_vec() -> Vec<Self> {
42 vec![Self::Dark, Self::TokyoNight]
43 }
44}
45
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47#[derive(Debug, Clone)]
48pub struct Theme {
49 pub dark_mode: bool,
51 #[cfg_attr(feature = "serde", serde(skip))]
52 pub overlay_manager: OverlayManager,
53
54 pub image_tint_recommended: bool,
59 pub kind: ThemeKind,
60 pub style: Style,
61 pub colors: ThemeColors,
62 pub text_sizes: TextSizes,
63 pub window_frame: Frame,
65 pub frame1: Frame,
67 pub frame2: Frame,
69
70 pub frame1_visuals: FrameVisuals,
71 pub frame2_visuals: FrameVisuals,
72}
73
74impl Theme {
75 pub fn new(kind: ThemeKind) -> Self {
79 let theme = match kind {
80 ThemeKind::Dark => dark::theme(),
81 ThemeKind::TokyoNight => tokyo_night::theme(),
82 ThemeKind::Custom => panic!("{}", PANIC_MSG),
84 };
85
86 theme
87 }
88
89 pub fn set_window_frame_colors(&mut self) {
90 match self.kind {
91 ThemeKind::Dark => self.window_frame = dark::window_frame(&self.colors),
92 ThemeKind::TokyoNight => self.window_frame = tokyo_night::window_frame(&self.colors),
93 ThemeKind::Custom => panic!("{}", PANIC_MSG),
95 }
96 }
97
98 pub fn set_frame1_colors(&mut self) {
99 match self.kind {
100 ThemeKind::Dark => self.frame1 = dark::frame1(&self.colors),
101 ThemeKind::TokyoNight => self.frame1 = tokyo_night::frame1(&self.colors),
102 ThemeKind::Custom => panic!("{}", PANIC_MSG),
104 }
105 }
106
107 pub fn set_frame2_colors(&mut self) {
108 match self.kind {
109 ThemeKind::Dark => self.frame2 = dark::frame2(&self.colors),
110 ThemeKind::TokyoNight => self.frame2 = tokyo_night::frame2(&self.colors),
111 ThemeKind::Custom => panic!("{}", PANIC_MSG),
113 }
114 }
115
116 pub fn button_visuals(&self) -> ButtonVisuals {
117 match self.kind {
118 ThemeKind::Dark => self.colors.button_visuals,
119 ThemeKind::TokyoNight => self.colors.button_visuals,
120 ThemeKind::Custom => panic!("{}", PANIC_MSG),
122 }
123 }
124
125 pub fn label_visuals(&self) -> LabelVisuals {
126 match self.kind {
127 ThemeKind::Dark => self.colors.label_visuals,
128 ThemeKind::TokyoNight => self.colors.label_visuals,
129 ThemeKind::Custom => panic!("{}", PANIC_MSG),
131 }
132 }
133
134 pub fn combo_box_visuals(&self) -> ComboBoxVisuals {
135 match self.kind {
136 ThemeKind::Dark => self.colors.combo_box_visuals,
137 ThemeKind::TokyoNight => self.colors.combo_box_visuals,
138 ThemeKind::Custom => panic!("{}", PANIC_MSG),
140 }
141 }
142
143 pub fn text_edit_visuals(&self) -> TextEditVisuals {
144 match self.kind {
145 ThemeKind::Dark => self.colors.text_edit_visuals,
146 ThemeKind::TokyoNight => self.colors.text_edit_visuals,
147 ThemeKind::Custom => panic!("{}", PANIC_MSG),
149 }
150 }
151}
152
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
155#[derive(Copy, Clone, Debug)]
156pub struct ThemeColors {
157 pub button_visuals: ButtonVisuals,
158
159 pub label_visuals: LabelVisuals,
160
161 pub combo_box_visuals: ComboBoxVisuals,
162
163 pub text_edit_visuals: TextEditVisuals,
164
165 pub title_bar: Color32,
167
168 pub bg: Color32,
170
171 pub widget_bg: Color32,
175
176 pub hover: Color32,
178
179 pub text: Color32,
181
182 pub text_muted: Color32,
186
187 pub highlight: Color32,
189
190 pub border: Color32,
192
193 pub accent: Color32,
195
196 pub error: Color32,
200
201 pub warning: Color32,
203
204 pub success: Color32,
208
209 pub info: Color32,
213}
214
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216#[derive(Clone, Default, Debug)]
217pub struct TextSizes {
218 pub very_small: f32,
219 pub small: f32,
220 pub normal: f32,
221 pub large: f32,
222 pub very_large: f32,
223 pub heading: f32,
224}
225
226impl TextSizes {
227 pub fn new(
228 very_small: f32,
229 small: f32,
230 normal: f32,
231 large: f32,
232 very_large: f32,
233 heading: f32,
234 ) -> Self {
235 Self {
236 very_small,
237 small,
238 normal,
239 large,
240 very_large,
241 heading,
242 }
243 }
244}
245
246#[derive(Clone, Debug, Default)]
247pub struct OverlayManager(Arc<RwLock<OverlayCounter>>);
248
249impl OverlayManager {
250 pub fn new() -> Self {
251 Self(Arc::new(RwLock::new(OverlayCounter::new())))
252 }
253
254 pub fn tint_0(&self) -> Color32 {
255 Color32::from_black_alpha(40)
256 }
257
258 pub fn tint_1(&self) -> Color32 {
259 Color32::from_black_alpha(60)
260 }
261
262 pub fn tint_2(&self) -> Color32 {
263 Color32::from_black_alpha(80)
264 }
265
266 pub fn tint_3(&self) -> Color32 {
267 Color32::from_black_alpha(100)
268 }
269
270 pub fn counter(&self) -> u8 {
271 self.0.read().unwrap().counter()
272 }
273
274 pub fn order(&self) -> Order {
275 self.0.read().unwrap().order()
276 }
277
278 pub fn paint_background(&self) {
279 self.0.write().unwrap().paint_background()
280 }
281
282 pub fn paint_middle(&self) {
283 self.0.write().unwrap().paint_middle()
284 }
285
286 pub fn paint_foreground(&self) {
287 self.0.write().unwrap().paint_foreground()
288 }
289
290 pub fn paint_tooltip(&self) {
291 self.0.write().unwrap().paint_tooltip()
292 }
293
294 pub fn paint_debug(&self) {
295 self.0.write().unwrap().paint_debug()
296 }
297
298 pub fn window_opened(&self) {
300 self.0.write().unwrap().window_opened();
301 }
302
303 pub fn window_closed(&self) {
305 self.0.write().unwrap().window_closed();
306 }
307
308 pub fn recommended_order(&self) -> Order {
309 self.0.read().unwrap().recommended_order()
310 }
311
312 pub fn calculate_alpha(&self) -> u8 {
313 self.0.read().unwrap().calculate_alpha()
314 }
315
316 pub fn overlay_tint(&self) -> Color32 {
318 self.0.read().unwrap().overlay_tint()
319 }
320
321 pub fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
325 self.0.read().unwrap().paint_overlay(ctx, recommend_order);
326 }
327
328 pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
330 self.0.read().unwrap().paint_overlay_at(ctx, rect, order, id, tint);
331 }
332}
333
334#[derive(Clone, Debug)]
335struct OverlayCounter {
336 counter: u8,
337 order: Order,
338}
339
340impl Default for OverlayCounter {
341 fn default() -> Self {
342 Self::new()
343 }
344}
345
346impl OverlayCounter {
347 pub fn new() -> Self {
348 Self {
349 counter: 0,
350 order: Order::Background,
351 }
352 }
353
354 pub fn counter(&self) -> u8 {
355 self.counter
356 }
357
358 pub fn order(&self) -> Order {
359 self.order
360 }
361
362 fn paint_background(&mut self) {
363 self.order = Order::Background;
364 }
365
366 fn paint_middle(&mut self) {
367 self.order = Order::Middle;
368 }
369
370 fn paint_foreground(&mut self) {
371 self.order = Order::Foreground;
372 }
373
374 fn paint_tooltip(&mut self) {
375 self.order = Order::Tooltip;
376 }
377
378 fn paint_debug(&mut self) {
379 self.order = Order::Debug;
380 }
381
382 fn window_opened(&mut self) {
383 self.counter += 1;
384 }
385
386 fn window_closed(&mut self) {
387 if self.counter > 0 {
388 self.counter -= 1;
389 }
390 }
391
392 fn calculate_alpha(&self) -> u8 {
393 let counter = self.counter;
394
395 if counter == 0 {
396 return 0;
397 }
398
399 let mut a = 60;
400 for _ in 1..counter {
401 a += 20;
402 }
403
404 a
405 }
406
407 fn overlay_tint(&self) -> Color32 {
408 let counter = self.counter();
409
410 if counter == 1 {
411 return Color32::from_black_alpha(60);
412 }
413
414 let alpha = self.calculate_alpha();
415 Color32::from_black_alpha(alpha)
416 }
417
418 fn recommended_order(&self) -> Order {
419 if self.counter() == 1 {
420 Order::Middle
421 } else if self.counter() == 2 {
422 Order::Foreground
423 } else {
424 Order::Tooltip
425 }
426 }
427
428 fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
429 let counter = self.counter();
430 if counter == 0 {
431 return;
432 }
433
434 let order = if recommend_order {
435 if counter == 1 {
436 Order::Middle
437 } else if counter == 2 {
438 Order::Foreground
439 } else {
440 Order::Tooltip
441 }
442 } else {
443 self.order()
444 };
445
446 let layer_id = LayerId::new(order, Id::new("darkening_overlay"));
447
448 let painter = ctx.layer_painter(layer_id);
449 painter.rect_filled(ctx.content_rect(), 0.0, self.overlay_tint());
450 }
451
452 pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
453 let layer_id = LayerId::new(order, id);
454
455 let painter = ctx.layer_painter(layer_id);
456 painter.rect_filled(rect, 0.0, tint);
457 }
458}