1use eframe::egui;
3
4const ACCENT: egui::Color32 = egui::Color32::from_rgb(11, 147, 135);
5pub(crate) const BG: egui::Color32 = egui::Color32::from_rgb(11, 14, 17);
6const PANEL: egui::Color32 = egui::Color32::from_rgb(20, 24, 29);
7const PANEL_STROKE: egui::Color32 = egui::Color32::from_rgb(42, 49, 56);
8pub(crate) const TEXT: egui::Color32 = egui::Color32::from_rgb(230, 233, 236);
9pub(crate) const FAINT: egui::Color32 = egui::Color32::from_rgb(154, 163, 173);
10const INACTIVE: egui::Color32 = egui::Color32::from_rgb(23, 28, 34);
11const HOVERED: egui::Color32 = egui::Color32::from_rgb(31, 38, 46);
12const ACTIVE: egui::Color32 = egui::Color32::from_rgb(30, 58, 53);
13
14pub fn apply(ctx: &egui::Context) {
15 ctx.set_fonts(fonts());
16 ctx.set_visuals(visuals());
17 ctx.global_style_mut(|style| {
18 style.spacing.item_spacing = egui::vec2(8.0, 6.0);
19 style.spacing.button_padding = egui::vec2(10.0, 5.0);
20 style.spacing.indent = 20.0;
21 style.spacing.scroll.bar_width = 10.0;
22 style.text_styles.insert(
23 egui::TextStyle::Body,
24 egui::FontId::new(15.0, egui::FontFamily::Proportional),
25 );
26 style.text_styles.insert(
27 egui::TextStyle::Button,
28 egui::FontId::new(15.0, egui::FontFamily::Proportional),
29 );
30 style.text_styles.insert(
31 egui::TextStyle::Heading,
32 egui::FontId::new(20.0, egui::FontFamily::Proportional),
33 );
34 style.text_styles.insert(
35 egui::TextStyle::Monospace,
36 egui::FontId::new(14.0, egui::FontFamily::Monospace),
37 );
38 style.text_styles.insert(
39 egui::TextStyle::Small,
40 egui::FontId::new(12.0, egui::FontFamily::Proportional),
41 );
42 });
43}
44
45fn fonts() -> egui::FontDefinitions {
46 let mut fonts = egui::FontDefinitions::default();
47 fonts.font_data.insert(
48 "Inter".to_string(),
49 egui::FontData::from_static(pite_render::text::FONT_BYTES).into(),
50 );
51 fonts.font_data.insert(
52 "JetBrainsMono".to_string(),
53 egui::FontData::from_static(include_bytes!("../fonts/JetBrainsMono-Regular.ttf")).into(),
54 );
55 fonts
56 .families
57 .insert(egui::FontFamily::Proportional, vec!["Inter".to_string()]);
58 fonts.families.insert(
59 egui::FontFamily::Monospace,
60 vec!["JetBrainsMono".to_string()],
61 );
62 fonts
63}
64
65fn widget(fill: egui::Color32) -> egui::style::WidgetVisuals {
66 egui::style::WidgetVisuals {
67 bg_fill: fill,
68 weak_bg_fill: fill,
69 bg_stroke: egui::Stroke::new(1.0_f32, PANEL_STROKE),
70 fg_stroke: egui::Stroke::new(1.0_f32, TEXT),
71 corner_radius: 6.0.into(),
72 expansion: 0.0,
73 }
74}
75
76fn visuals() -> egui::Visuals {
77 let mut visuals = egui::Visuals::dark();
78 visuals.dark_mode = true;
79 visuals.override_text_color = Some(TEXT);
80 visuals.weak_text_color = Some(FAINT);
81 visuals.faint_bg_color = PANEL;
82 visuals.extreme_bg_color = BG;
83 visuals.code_bg_color = BG;
84 visuals.window_fill = PANEL;
85 visuals.panel_fill = PANEL;
86 visuals.window_stroke = egui::Stroke::new(1.0_f32, PANEL_STROKE);
87 visuals.hyperlink_color = ACCENT;
88 visuals.selection.bg_fill = egui::Color32::from_rgb(18, 77, 73);
93 visuals.selection.stroke = egui::Stroke::new(1.0_f32, TEXT);
94 visuals.widgets.noninteractive = widget(PANEL);
95 visuals.widgets.inactive = widget(INACTIVE);
96 visuals.widgets.hovered = widget(HOVERED);
97 visuals.widgets.active = widget(ACTIVE);
98 visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0_f32, ACCENT);
99 visuals.widgets.open = widget(INACTIVE);
100 visuals.window_corner_radius = 8.0.into();
101 visuals.menu_corner_radius = 6.0.into();
102 visuals
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn theme_applies_cleanly() {
111 let ctx = egui::Context::default();
112 apply(&ctx);
113 let style = ctx.global_style();
114 assert!(style.visuals.dark_mode);
115 assert_eq!(style.visuals.window_fill, PANEL);
116 assert_eq!(style.text_styles[&egui::TextStyle::Body].size, 15.0);
117 assert_eq!(style.visuals.selection.stroke.color, TEXT);
118 }
119}