xtap_core_lib/ui/
theme.rs1use chrono::Timelike;
7use egui::{Color32, Margin, Visuals};
8
9pub struct UiTheme;
11
12#[derive(Debug, Clone, Copy, PartialEq, serde::Deserialize, serde::Serialize)]
14pub enum ThemeMode {
15 Auto,
16 Light,
17 Dark,
18}
19
20impl UiTheme {
21 pub const PADDING: f32 = 20.0;
23 pub const SPACING: f32 = 12.0;
24 pub const ROUNDING: u8 = 12; pub const BUTTON_HEIGHT: f32 = 36.0;
26 pub const ICON_SIZE: f32 = 18.0;
27
28 pub const PRIMARY: Color32 = Color32::from_rgb(0, 120, 215);
30 pub const SUCCESS: Color32 = Color32::from_rgb(16, 124, 16);
31 pub const ERROR: Color32 = Color32::from_rgb(209, 52, 56);
32
33 pub fn standard_margin() -> Margin {
35 Margin {
36 left: Self::PADDING as i8,
37 right: Self::PADDING as i8,
38 top: Self::PADDING as i8,
39 bottom: Self::PADDING as i8,
40 }
41 }
42
43 pub fn apply_master_visuals(ctx: &egui::Context, mode: ThemeMode) {
45 let is_dark = match mode {
46 ThemeMode::Light => false,
47 ThemeMode::Dark => true,
48 ThemeMode::Auto => {
49 let hour = chrono::Local::now().hour();
50 !(6..18).contains(&hour) }
52 };
53
54 let mut visuals = if is_dark {
55 let mut v = Visuals::dark();
56 v.panel_fill = Color32::from_rgb(18, 20, 24);
58 v.widgets.noninteractive.bg_fill = Color32::from_rgb(30, 33, 37);
59 v.widgets.noninteractive.weak_bg_fill = Color32::from_rgb(40, 44, 49);
60 v.extreme_bg_color = Color32::from_rgb(12, 14, 16);
61 v.widgets.noninteractive.fg_stroke.color = Color32::from_gray(200);
62 v.widgets.noninteractive.bg_stroke.color = Color32::from_gray(50); v.widgets.inactive.corner_radius = 12.0.into();
66 v.widgets.hovered.corner_radius = 12.0.into();
67 v.widgets.active.corner_radius = 12.0.into();
68 v
69 } else {
70 let mut v = Visuals::light();
71 v.panel_fill = Color32::from_rgb(248, 249, 250);
73 v.widgets.noninteractive.bg_fill = Color32::from_rgb(240, 242, 245);
74 v.widgets.noninteractive.weak_bg_fill = Color32::from_rgb(255, 255, 255);
75 v.extreme_bg_color = Color32::from_rgb(230, 233, 237);
76 v.widgets.noninteractive.fg_stroke.color = Color32::from_rgb(45, 45, 45); v.widgets.noninteractive.bg_stroke.color = Color32::from_gray(200); v.widgets.inactive.corner_radius = 12.0.into();
81 v.widgets.hovered.corner_radius = 12.0.into();
82 v.widgets.active.corner_radius = 12.0.into();
83 v
84 };
85
86 visuals.window_corner_radius = 15.0.into();
88
89 visuals.selection.bg_fill = Self::PRIMARY;
91
92 ctx.set_visuals(visuals);
93 }
94
95 pub fn center_content<R>(
97 ui: &mut egui::Ui,
98 add_contents: impl FnOnce(&mut egui::Ui) -> R,
99 ) -> R {
100 ui.vertical_centered(|ui| {
101 ui.add_space(ui.available_height() / 3.0);
102 add_contents(ui)
103 })
104 .inner
105 }
106}