1use eframe::egui::{self, Color32, Rounding, Margin, Stroke};
7
8pub struct ThemeColors;
10
11impl ThemeColors {
12 pub const BG_DARK: Color32 = Color32::from_rgb(18, 18, 22);
14 pub const BG_HEADER: Color32 = Color32::from_rgb(22, 22, 26);
15 pub const BG_CARD: Color32 = Color32::from_rgb(32, 32, 38);
16 pub const BG_CARD_HOVER: Color32 = Color32::from_rgb(38, 38, 45);
17 pub const BG_INPUT: Color32 = Color32::from_rgb(38, 38, 45);
18 pub const BG_ICON: Color32 = Color32::from_rgb(45, 45, 52);
19
20 pub const RUNNING_BG: Color32 = Color32::from_rgb(25, 35, 30);
22 pub const RUNNING_BORDER: Color32 = Color32::from_rgb(34, 197, 94);
23 pub const RUNNING_BADGE_BG: Color32 = Color32::from_rgb(34, 55, 40);
24 pub const RUNNING_TEXT: Color32 = Color32::from_rgb(80, 220, 120);
25
26 pub const LOADING_BG: Color32 = Color32::from_rgb(25, 30, 40);
28 pub const LOADING_BORDER: Color32 = Color32::from_rgb(99, 102, 241);
29
30 pub const BTN_PRIMARY: Color32 = Color32::from_rgb(99, 102, 241); pub const BTN_DANGER: Color32 = Color32::from_rgb(239, 68, 68); pub const BTN_WARNING: Color32 = Color32::from_rgb(245, 158, 11); pub const BTN_DELETE: Color32 = Color32::from_rgb(180, 80, 80);
35 pub const BTN_DELETE_BG: Color32 = Color32::from_rgb(60, 40, 40);
36
37 pub const TEXT_PRIMARY: Color32 = Color32::WHITE;
39 pub const TEXT_SECONDARY: Color32 = Color32::from_rgb(160, 160, 170);
40 pub const TEXT_MUTED: Color32 = Color32::from_rgb(100, 100, 110);
41 pub const TEXT_SUBTLE: Color32 = Color32::from_rgb(80, 80, 90);
42
43 pub const BORDER_DEFAULT: Color32 = Color32::from_rgb(55, 55, 62);
45}
46
47pub struct ThemeSpacing;
49
50impl ThemeSpacing {
51 pub const CARD_WIDTH: f32 = 250.0;
52 pub const CARD_HEIGHT: f32 = 240.0;
53 pub const CARD_PADDING: f32 = 18.0;
54 pub const CARD_ROUNDING: f32 = 16.0;
55 pub const CARD_GRID_SPACING: f32 = 16.0;
56
57 pub const BUTTON_ROUNDING: f32 = 10.0;
58 pub const BUTTON_HEIGHT: f32 = 36.0;
59
60 pub const ICON_SIZE: f32 = 32.0;
61 pub const ICON_CONTAINER_SIZE: f32 = 44.0;
62}
63
64pub fn apply_theme(ctx: &egui::Context) {
66 let mut visuals = egui::Visuals::dark();
67
68 visuals.window_rounding = Rounding::same(12.0);
69 visuals.window_shadow = egui::epaint::Shadow {
70 offset: egui::vec2(0.0, 8.0),
71 blur: 24.0,
72 spread: 0.0,
73 color: Color32::from_black_alpha(100),
74 };
75 visuals.popup_shadow = visuals.window_shadow;
76
77 visuals.widgets.noninteractive.bg_fill = Color32::from_rgb(28, 28, 32);
78 visuals.widgets.inactive.bg_fill = Color32::from_rgb(45, 45, 50);
79 visuals.widgets.inactive.weak_bg_fill = Color32::from_rgb(45, 45, 50);
80 visuals.widgets.hovered.bg_fill = Color32::from_rgb(60, 60, 68);
81 visuals.widgets.active.bg_fill = Color32::from_rgb(70, 70, 78);
82 visuals.selection.bg_fill = Color32::from_rgb(0, 120, 215);
83 visuals.extreme_bg_color = Color32::from_rgb(20, 20, 24);
84 visuals.faint_bg_color = Color32::from_rgb(35, 35, 40);
85
86 ctx.set_visuals(visuals);
87
88 let mut style = (*ctx.style()).clone();
90 style.spacing.item_spacing = egui::vec2(10.0, 8.0);
91 style.spacing.button_padding = egui::vec2(12.0, 6.0);
92 style.spacing.window_margin = Margin::same(16.0);
93 style.visuals.widgets.noninteractive.rounding = Rounding::same(8.0);
94 style.visuals.widgets.inactive.rounding = Rounding::same(8.0);
95 style.visuals.widgets.hovered.rounding = Rounding::same(8.0);
96 style.visuals.widgets.active.rounding = Rounding::same(8.0);
97 ctx.set_style(style);
98}
99
100pub fn get_card_colors(is_running: bool, is_loading: bool) -> (Color32, Color32, Color32) {
102 if is_running {
103 (
104 ThemeColors::RUNNING_BG,
105 ThemeColors::RUNNING_BORDER,
106 Color32::from_rgba_unmultiplied(34, 197, 94, 30),
107 )
108 } else if is_loading {
109 (
110 ThemeColors::LOADING_BG,
111 ThemeColors::LOADING_BORDER,
112 Color32::from_rgba_unmultiplied(99, 102, 241, 30),
113 )
114 } else {
115 (
116 ThemeColors::BG_CARD,
117 ThemeColors::BORDER_DEFAULT,
118 Color32::TRANSPARENT,
119 )
120 }
121}
122
123pub fn card_frame(bg_color: Color32, border_color: Color32, glow_color: Color32) -> egui::Frame {
125 egui::Frame::none()
126 .fill(bg_color)
127 .rounding(ThemeSpacing::CARD_ROUNDING)
128 .inner_margin(Margin::same(ThemeSpacing::CARD_PADDING))
129 .stroke(Stroke::new(1.5, border_color))
130 .shadow(egui::epaint::Shadow {
131 offset: egui::vec2(0.0, 4.0),
132 blur: 12.0,
133 spread: 0.0,
134 color: glow_color,
135 })
136}
137
138pub fn primary_button(text: &str) -> egui::Button<'_> {
140 egui::Button::new(
141 egui::RichText::new(text)
142 .size(14.0)
143 .color(ThemeColors::TEXT_PRIMARY),
144 )
145 .fill(ThemeColors::BTN_PRIMARY)
146 .rounding(ThemeSpacing::BUTTON_ROUNDING)
147}
148
149pub fn action_button(text: &str, color: Color32) -> egui::Button<'_> {
151 egui::Button::new(
152 egui::RichText::new(text)
153 .size(12.0)
154 .color(ThemeColors::TEXT_PRIMARY),
155 )
156 .fill(color)
157 .rounding(ThemeSpacing::BUTTON_ROUNDING)
158}