zeus_theme/
window.rs

1use super::{Theme, utils};
2use egui::{
3   self, Button, CentralPanel, Color32, Rect, RichText, UiBuilder, ViewportCommand, vec2,
4};
5
6/// A frame for the App's Native window
7pub fn window_frame(
8   ctx: &egui::Context,
9   title: &str,
10   theme: Theme,
11   add_contents: impl FnOnce(&mut egui::Ui),
12) {
13   CentralPanel::default().frame(theme.window_frame).show(ctx, |ui| {
14      let app_rect = ui.max_rect();
15
16      let title_bar_height = 32.0;
17      let title_bar_rect = {
18         let mut rect = app_rect;
19         rect.max.y = rect.min.y + title_bar_height;
20         rect
21      };
22
23      title_bar_ui(ui, &theme, title_bar_rect, title);
24
25      // Add the contents:
26      let content_rect = {
27         let mut rect = app_rect;
28         rect.min.y = title_bar_rect.max.y;
29         rect
30      }
31      .shrink(4.0);
32
33      let ui_builder = UiBuilder::default().max_rect(content_rect).style(ctx.style().clone());
34      let mut content_ui = ui.new_child(ui_builder);
35      add_contents(&mut content_ui);
36   });
37}
38
39fn title_bar_ui(ui: &mut egui::Ui, theme: &Theme, title_bar_rect: Rect, title: &str) {
40   use egui::*;
41
42   let painter = ui.painter();
43
44   let title_bar_response = ui.interact(
45      title_bar_rect,
46      Id::new("title_bar"),
47      Sense::click_and_drag(),
48   );
49
50   let title_size = theme.text_sizes.heading;
51   let title_color = theme.colors.text;
52
53   // Paint the title:
54   painter.text(
55      title_bar_rect.center(),
56      Align2::CENTER_CENTER,
57      title,
58      FontId::proportional(title_size),
59      title_color,
60   );
61
62   // Paint the line under the title:
63   painter.line_segment(
64      [
65         title_bar_rect.left_bottom() + vec2(1.0, 0.0),
66         title_bar_rect.right_bottom() + vec2(-1.0, 0.0),
67      ],
68      ui.visuals().widgets.noninteractive.bg_stroke,
69   );
70
71   // Interact with the title bar (drag to move window):
72   if title_bar_response.double_clicked() {
73      let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
74      ui.ctx().send_viewport_cmd(ViewportCommand::Maximized(!is_maximized));
75   }
76
77   if title_bar_response.drag_started_by(PointerButton::Primary) {
78      ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag);
79   }
80
81   let ui_builder = UiBuilder::default().max_rect(title_bar_rect).style(theme.style.clone());
82
83   ui.scope_builder(ui_builder, |ui| {
84      ui.with_layout(
85         egui::Layout::right_to_left(egui::Align::Center),
86         |ui| {
87            ui.add_space(5.0);
88            close_maximize_minimize(ui, &theme);
89         },
90      );
91   });
92}
93
94/// Show some close/maximize/minimize buttons for the native window.
95fn close_maximize_minimize(ui: &mut egui::Ui, theme: &Theme) {
96   utils::bg_color_on_idle(ui, Color32::TRANSPARENT);
97   utils::no_border(ui);
98   ui.style_mut().visuals.widgets.hovered.expansion = 5.0;
99
100   let button_size = vec2(20.0, 15.0);
101   let color = theme.colors.text;
102
103   ui.scope(|ui| {
104      utils::bg_color_on_hover(ui, theme.colors.error);
105      let text = RichText::new("❌").color(color).size(theme.text_sizes.large);
106      let close_button = Button::new(text).min_size(button_size);
107      let close_response = ui.add(close_button);
108
109      if close_response.clicked() {
110         ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
111      }
112   });
113
114   let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
115   utils::bg_color_on_hover(ui, theme.colors.bg3);
116
117   if is_maximized {
118      let text = RichText::new("🗗").color(color).size(theme.text_sizes.large);
119      let maximized_button = Button::new(text).min_size(button_size);
120      let maximized_response = ui.add(maximized_button);
121
122      if maximized_response.clicked() {
123         ui.ctx().send_viewport_cmd(ViewportCommand::Maximized(false));
124      }
125   } else {
126      let text = RichText::new("🗗").color(color).size(theme.text_sizes.large);
127      let maximized_button = Button::new(text).min_size(button_size);
128      let maximized_response = ui.add(maximized_button);
129
130      if maximized_response.clicked() {
131         ui.ctx().send_viewport_cmd(ViewportCommand::Maximized(true));
132      }
133   }
134
135   let text = RichText::new("🗕").color(color).size(theme.text_sizes.large);
136   let minimized_button = Button::new(text).min_size(button_size);
137   let minimized_response = ui.add(minimized_button);
138
139   if minimized_response.clicked() {
140      ui.ctx().send_viewport_cmd(ViewportCommand::Minimized(true));
141   }
142}