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 Custom,
27}
28
29impl ThemeKind {
30 pub fn to_str(&self) -> &str {
31 match self {
32 ThemeKind::Dark => "Dark",
33 ThemeKind::Custom => "Custom",
35 }
36 }
37
38 pub fn to_vec() -> Vec<Self> {
39 vec![Self::Dark]
40 }
41}
42
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44#[derive(Debug, Clone)]
45pub struct Theme {
46 pub dark_mode: bool,
48 #[cfg_attr(feature = "serde", serde(skip))]
49 pub overlay_manager: OverlayManager,
50
51 pub image_tint_recommended: bool,
56 pub kind: ThemeKind,
57 pub style: Style,
58 pub colors: ThemeColors,
59 pub text_sizes: TextSizes,
60 pub window_frame: Frame,
62 pub frame1: Frame,
64 pub frame2: Frame,
66
67 pub frame1_visuals: FrameVisuals,
68 pub frame2_visuals: FrameVisuals,
69}
70
71impl Theme {
72 pub fn new(kind: ThemeKind) -> Self {
76 let theme = match kind {
77 ThemeKind::Dark => dark::theme(),
78 ThemeKind::Custom => panic!("{}", PANIC_MSG),
80 };
81
82 theme
83 }
84
85 pub fn set_window_frame_colors(&mut self) {
86 match self.kind {
87 ThemeKind::Dark => self.window_frame = dark::window_frame(&self.colors),
88 ThemeKind::Custom => panic!("{}", PANIC_MSG),
90 }
91 }
92
93 pub fn set_frame1_colors(&mut self) {
94 match self.kind {
95 ThemeKind::Dark => self.frame1 = dark::frame1(&self.colors),
96 ThemeKind::Custom => panic!("{}", PANIC_MSG),
98 }
99 }
100
101 pub fn set_frame2_colors(&mut self) {
102 match self.kind {
103 ThemeKind::Dark => self.frame2 = dark::frame2(&self.colors),
104 ThemeKind::Custom => panic!("{}", PANIC_MSG),
106 }
107 }
108
109 pub fn button_visuals(&self) -> ButtonVisuals {
110 match self.kind {
111 ThemeKind::Dark => self.colors.button_visuals,
112 ThemeKind::Custom => panic!("{}", PANIC_MSG),
114 }
115 }
116
117 pub fn label_visuals(&self) -> LabelVisuals {
118 match self.kind {
119 ThemeKind::Dark => self.colors.label_visuals,
120 ThemeKind::Custom => panic!("{}", PANIC_MSG),
122 }
123 }
124
125 pub fn combo_box_visuals(&self) -> ComboBoxVisuals {
126 match self.kind {
127 ThemeKind::Dark => self.colors.combo_box_visuals,
128 ThemeKind::Custom => panic!("{}", PANIC_MSG),
130 }
131 }
132
133 pub fn text_edit_visuals(&self) -> TextEditVisuals {
134 match self.kind {
135 ThemeKind::Dark => self.colors.text_edit_visuals,
136 ThemeKind::Custom => panic!("{}", PANIC_MSG),
138 }
139 }
140}
141
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
144#[derive(Copy, Clone, Debug)]
145pub struct ThemeColors {
146 pub button_visuals: ButtonVisuals,
147
148 pub label_visuals: LabelVisuals,
149
150 pub combo_box_visuals: ComboBoxVisuals,
151
152 pub text_edit_visuals: TextEditVisuals,
153
154 pub title_bar: Color32,
156
157 pub bg: Color32,
159
160 pub widget_bg: Color32,
164
165 pub hover: Color32,
167
168 pub text: Color32,
170
171 pub text_muted: Color32,
175
176 pub highlight: Color32,
178
179 pub border: Color32,
181
182 pub accent: Color32,
184
185 pub error: Color32,
189
190 pub warning: Color32,
192
193 pub success: Color32,
197
198 pub info: Color32,
202}
203
204#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
205#[derive(Clone, Default, Debug)]
206pub struct TextSizes {
207 pub very_small: f32,
208 pub small: f32,
209 pub normal: f32,
210 pub large: f32,
211 pub very_large: f32,
212 pub heading: f32,
213}
214
215impl TextSizes {
216 pub fn new(
217 very_small: f32,
218 small: f32,
219 normal: f32,
220 large: f32,
221 very_large: f32,
222 heading: f32,
223 ) -> Self {
224 Self {
225 very_small,
226 small,
227 normal,
228 large,
229 very_large,
230 heading,
231 }
232 }
233}
234
235#[derive(Clone, Debug, Default)]
236pub struct OverlayManager(Arc<RwLock<OverlayCounter>>);
237
238impl OverlayManager {
239 pub fn new() -> Self {
240 Self(Arc::new(RwLock::new(OverlayCounter::new())))
241 }
242
243 pub fn tint_0(&self) -> Color32 {
244 Color32::from_black_alpha(40)
245 }
246
247 pub fn tint_1(&self) -> Color32 {
248 Color32::from_black_alpha(60)
249 }
250
251 pub fn tint_2(&self) -> Color32 {
252 Color32::from_black_alpha(80)
253 }
254
255 pub fn tint_3(&self) -> Color32 {
256 Color32::from_black_alpha(100)
257 }
258
259 pub fn counter(&self) -> u8 {
260 self.0.read().unwrap().counter()
261 }
262
263 pub fn order(&self) -> Order {
264 self.0.read().unwrap().order()
265 }
266
267 pub fn paint_background(&self) {
268 self.0.write().unwrap().paint_background()
269 }
270
271 pub fn paint_middle(&self) {
272 self.0.write().unwrap().paint_middle()
273 }
274
275 pub fn paint_foreground(&self) {
276 self.0.write().unwrap().paint_foreground()
277 }
278
279 pub fn paint_tooltip(&self) {
280 self.0.write().unwrap().paint_tooltip()
281 }
282
283 pub fn paint_debug(&self) {
284 self.0.write().unwrap().paint_debug()
285 }
286
287 pub fn window_opened(&self) {
289 self.0.write().unwrap().window_opened();
290 }
291
292 pub fn window_closed(&self) {
294 self.0.write().unwrap().window_closed();
295 }
296
297 pub fn recommended_order(&self) -> Order {
298 self.0.read().unwrap().recommended_order()
299 }
300
301 pub fn calculate_alpha(&self) -> u8 {
302 self.0.read().unwrap().calculate_alpha()
303 }
304
305 pub fn overlay_tint(&self) -> Color32 {
307 self.0.read().unwrap().overlay_tint()
308 }
309
310 pub fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
314 self.0.read().unwrap().paint_overlay(ctx, recommend_order);
315 }
316
317 pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
319 self.0.read().unwrap().paint_overlay_at(ctx, rect, order, id, tint);
320 }
321}
322
323#[derive(Clone, Debug)]
324struct OverlayCounter {
325 counter: u8,
326 order: Order,
327}
328
329impl Default for OverlayCounter {
330 fn default() -> Self {
331 Self::new()
332 }
333}
334
335impl OverlayCounter {
336 pub fn new() -> Self {
337 Self {
338 counter: 0,
339 order: Order::Background,
340 }
341 }
342
343 pub fn counter(&self) -> u8 {
344 self.counter
345 }
346
347 pub fn order(&self) -> Order {
348 self.order
349 }
350
351 fn paint_background(&mut self) {
352 self.order = Order::Background;
353 }
354
355 fn paint_middle(&mut self) {
356 self.order = Order::Middle;
357 }
358
359 fn paint_foreground(&mut self) {
360 self.order = Order::Foreground;
361 }
362
363 fn paint_tooltip(&mut self) {
364 self.order = Order::Tooltip;
365 }
366
367 fn paint_debug(&mut self) {
368 self.order = Order::Debug;
369 }
370
371 fn window_opened(&mut self) {
372 self.counter += 1;
373 }
374
375 fn window_closed(&mut self) {
376 if self.counter > 0 {
377 self.counter -= 1;
378 }
379 }
380
381 fn calculate_alpha(&self) -> u8 {
382 let counter = self.counter;
383
384 if counter == 0 {
385 return 0;
386 }
387
388 let mut a = 60;
389 for _ in 1..counter {
390 a += 20;
391 }
392
393 a
394 }
395
396 fn overlay_tint(&self) -> Color32 {
397 let counter = self.counter();
398
399 if counter == 1 {
400 return Color32::from_black_alpha(60);
401 }
402
403 let alpha = self.calculate_alpha();
404 Color32::from_black_alpha(alpha)
405 }
406
407 fn recommended_order(&self) -> Order {
408 if self.counter() == 1 {
409 Order::Middle
410 } else if self.counter() == 2 {
411 Order::Foreground
412 } else {
413 Order::Tooltip
414 }
415 }
416
417 fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
418 let counter = self.counter();
419 if counter == 0 {
420 return;
421 }
422
423 let order = if recommend_order {
424 if counter == 1 {
425 Order::Middle
426 } else if counter == 2 {
427 Order::Foreground
428 } else {
429 Order::Tooltip
430 }
431 } else {
432 self.order()
433 };
434
435 let layer_id = LayerId::new(order, Id::new("darkening_overlay"));
436
437 let painter = ctx.layer_painter(layer_id);
438 painter.rect_filled(ctx.content_rect(), 0.0, self.overlay_tint());
439 }
440
441 pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
442 let layer_id = LayerId::new(order, id);
443
444 let painter = ctx.layer_painter(layer_id);
445 painter.rect_filled(rect, 0.0, tint);
446 }
447}