khora_infra/ui/egui/
theme.rs1use khora_core::ui::UiTheme;
18
19pub(crate) const AXIS_COLORS_KEY: &str = "khora.axis_colors";
29
30fn c(color: [f32; 4]) -> egui::Color32 {
31 egui::Color32::from_rgba_unmultiplied(
32 (color[0] * 255.0) as u8,
33 (color[1] * 255.0) as u8,
34 (color[2] * 255.0) as u8,
35 (color[3] * 255.0) as u8,
36 )
37}
38
39pub fn apply_theme(ctx: &egui::Context, theme: &UiTheme) {
41 let mut visuals = egui::Visuals::dark();
42
43 visuals.panel_fill = c(theme.surface);
45 visuals.window_fill = c(theme.surface);
46 visuals.faint_bg_color = c(theme.surface_elevated);
47 visuals.extreme_bg_color = c(theme.background);
48 visuals.code_bg_color = c(theme.surface_elevated);
49
50 visuals.override_text_color = Some(c(theme.text));
52 visuals.warn_fg_color = c(theme.warning);
53 visuals.error_fg_color = c(theme.error);
54
55 visuals.selection.bg_fill = c(theme.primary).gamma_multiply(0.20);
57 visuals.selection.stroke = egui::Stroke::new(1.0_f32, c(theme.primary));
58 visuals.hyperlink_color = c(theme.accent_b);
59
60 visuals.window_shadow = egui::Shadow {
62 offset: [0, 8],
63 blur: 24,
64 spread: 0,
65 color: egui::Color32::from_black_alpha(140),
66 };
67 visuals.window_stroke = egui::Stroke::new(1.0_f32, c(theme.border));
68 visuals.popup_shadow = egui::Shadow {
69 offset: [0, 4],
70 blur: 12,
71 spread: 0,
72 color: egui::Color32::from_black_alpha(110),
73 };
74
75 let radius_md = egui::CornerRadius::same(theme.radius_md.round().clamp(0.0, 30.0) as u8);
77 let radius_sm = egui::CornerRadius::same(theme.radius_sm.round().clamp(0.0, 30.0) as u8);
78
79 visuals.widgets.noninteractive.bg_fill = c(theme.surface);
81 visuals.widgets.noninteractive.fg_stroke = egui::Stroke::new(1.0_f32, c(theme.text_dim));
82 visuals.widgets.noninteractive.corner_radius = radius_md;
83 visuals.widgets.noninteractive.bg_stroke = egui::Stroke::new(0.5_f32, c(theme.separator));
84
85 visuals.widgets.inactive.bg_fill = c(theme.surface_interactive);
86 visuals.widgets.inactive.fg_stroke = egui::Stroke::new(1.0_f32, c(theme.text));
87 visuals.widgets.inactive.corner_radius = radius_md;
88 visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0_f32, c(theme.border));
89
90 visuals.widgets.hovered.bg_fill = c(theme.surface_active);
91 visuals.widgets.hovered.fg_stroke = egui::Stroke::new(1.0_f32, c(theme.text));
92 visuals.widgets.hovered.corner_radius = radius_md;
93 visuals.widgets.hovered.bg_stroke = egui::Stroke::new(1.0_f32, c(theme.border_strong));
94
95 visuals.widgets.active.bg_fill = c(theme.primary).gamma_multiply(0.32);
96 visuals.widgets.active.fg_stroke = egui::Stroke::new(1.5_f32, c(theme.text));
97 visuals.widgets.active.corner_radius = radius_md;
98 visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0_f32, c(theme.primary));
99
100 visuals.widgets.open.bg_fill = c(theme.surface_elevated);
101 visuals.widgets.open.fg_stroke = egui::Stroke::new(1.0_f32, c(theme.text));
102 visuals.widgets.open.corner_radius = radius_md;
103
104 visuals.menu_corner_radius = radius_sm;
107
108 visuals.striped = false;
109 visuals.slider_trailing_fill = true;
110
111 ctx.set_visuals(visuals);
112
113 let axes: [egui::Color32; 3] = [c(theme.axis_x), c(theme.axis_y), c(theme.axis_z)];
115 ctx.data_mut(|d| d.insert_temp(egui::Id::new(AXIS_COLORS_KEY), axes));
116
117 let mut style = (*ctx.global_style()).clone();
119 style.spacing.item_spacing = egui::vec2(theme.pad_row * 0.75, theme.pad_row * 0.5);
120 style.spacing.button_padding = egui::vec2(10.0, 4.0);
121 style.spacing.indent = 14.0;
122 style.spacing.scroll.bar_width = 8.0;
123 style.spacing.window_margin = egui::Margin::same(8);
124 style.spacing.menu_margin = egui::Margin::symmetric(8, 6);
125 style.interaction.resize_grab_radius_side = 8.0;
129 style.interaction.resize_grab_radius_corner = 10.0;
130
131 use egui::{FontFamily, FontId, TextStyle};
134 style.text_styles.insert(
135 TextStyle::Heading,
136 FontId::new(theme.font_size_display, FontFamily::Proportional),
137 );
138 style.text_styles.insert(
139 TextStyle::Body,
140 FontId::new(theme.font_size_body, FontFamily::Proportional),
141 );
142 style.text_styles.insert(
143 TextStyle::Button,
144 FontId::new(theme.font_size_body, FontFamily::Proportional),
145 );
146 style.text_styles.insert(
147 TextStyle::Small,
148 FontId::new(theme.font_size_caption, FontFamily::Proportional),
149 );
150 style.text_styles.insert(
151 TextStyle::Monospace,
152 FontId::new(theme.font_size_body - 0.5, FontFamily::Monospace),
153 );
154
155 ctx.set_global_style(style);
156}