Skip to main content

zeus_theme/
window.rs

1use super::{Theme, utils};
2use egui::*;
3
4pub struct WindowCtx {
5   pub frame: Frame,
6   pub title: String,
7   pub bar_height: f32,
8   pub title_text_size: f32,
9   pub title_text_color: Color32,
10   pub line_stroke: Stroke,
11   pub button_text_size: f32,
12   pub button_text_color: Color32,
13   pub on_hover_color: Color32,
14   pub close_on_hover_color: Color32,
15}
16
17impl WindowCtx {
18   pub fn new(title: &str, bar_height: f32, theme: &Theme) -> Self {
19      let frame = theme.window_frame;
20      let title_text_size = theme.text_sizes.heading;
21      let title_text_color = theme.colors.text;
22      let line_stroke = Stroke::new(0.0, theme.colors.border);
23      let button_text_size = theme.text_sizes.large;
24      let button_text_color = theme.colors.text;
25      let on_hover_color = theme.colors.hover;
26      let close_on_hover_color = theme.colors.error;
27
28      Self {
29         frame,
30         title: title.to_owned(),
31         bar_height,
32         title_text_size,
33         title_text_color,
34         line_stroke,
35         button_text_size,
36         button_text_color,
37         on_hover_color,
38         close_on_hover_color,
39      }
40   }
41
42   pub fn with_frame(mut self, frame: Frame) -> Self {
43      self.frame = frame;
44      self
45   }
46
47   pub fn with_line_stroke(mut self, stroke: Stroke) -> Self {
48      self.line_stroke = stroke;
49      self
50   }
51
52   pub fn with_title_text_size(mut self, size: f32) -> Self {
53      self.title_text_size = size;
54      self
55   }
56
57   pub fn with_title_text_color(mut self, color: Color32) -> Self {
58      self.title_text_color = color;
59      self
60   }
61
62   pub fn with_button_text_color(mut self, color: Color32) -> Self {
63      self.button_text_color = color;
64      self
65   }
66
67   pub fn with_on_hover_color(mut self, color: Color32) -> Self {
68      self.on_hover_color = color;
69      self
70   }
71
72   pub fn with_close_on_hover_color(mut self, color: Color32) -> Self {
73      self.close_on_hover_color = color;
74      self
75   }
76}
77
78pub fn window_frame(ui: &mut Ui, window_ctx: WindowCtx, add_contents: impl FnOnce(&mut Ui)) {
79   CentralPanel::default().frame(window_ctx.frame).show_inside(ui, |ui| {
80      let app_rect = ui.max_rect();
81
82      let title_bar_rect = {
83         let mut rect = app_rect;
84         rect.max.y = window_ctx.bar_height;
85         rect
86      };
87
88      title_bar_ui(ui, &window_ctx, title_bar_rect);
89
90      // Add the contents
91      let content_rect = {
92         let mut rect = app_rect;
93         rect.min.y = window_ctx.bar_height;
94         rect
95      };
96
97      let ui_builder = UiBuilder::default().max_rect(content_rect).style(ui.global_style().clone());
98      let mut content_ui = ui.new_child(ui_builder);
99      add_contents(&mut content_ui);
100   });
101}
102
103fn title_bar_ui(ui: &mut Ui, window: &WindowCtx, title_bar_rect: Rect) {
104   let painter = ui.painter();
105
106   let title_bar_response = ui.interact(
107      title_bar_rect,
108      Id::new("title_bar"),
109      Sense::click_and_drag(),
110   );
111
112   // Paint the title:
113   painter.text(
114      title_bar_rect.center(),
115      Align2::CENTER_CENTER,
116      window.title.clone(),
117      FontId::proportional(window.title_text_size),
118      window.title_text_color,
119   );
120
121   // Paint the line under the title:
122   let y = title_bar_rect.bottom() - window.line_stroke.width / 2.0;
123   painter.line_segment(
124      [
125         pos2(title_bar_rect.left() + 1.0, y),
126         pos2(title_bar_rect.right() - 1.0, y),
127      ],
128      window.line_stroke,
129   );
130
131   // Interact with the title bar (drag to move window):
132   if title_bar_response.double_clicked() {
133      let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
134      ui.ctx().send_viewport_cmd(ViewportCommand::Maximized(!is_maximized));
135   }
136
137   if title_bar_response.drag_started_by(PointerButton::Primary) {
138      ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag);
139   }
140
141   let ui_builder = UiBuilder::default().max_rect(title_bar_rect).style(ui.style().clone());
142   let layout = Layout::right_to_left(Align::Center);
143
144   ui.scope_builder(ui_builder, |ui| {
145      ui.with_layout(layout, |ui| {
146         close_maximize_minimize(ui, &window);
147      });
148   });
149}
150
151/// Show some close/maximize/minimize buttons for the native window.
152fn close_maximize_minimize(ui: &mut Ui, window: &WindowCtx) {
153   ui.spacing_mut().button_padding = vec2(0.0, 0.0);
154   ui.spacing_mut().item_spacing = vec2(0.0, 0.0);
155
156   utils::bg_color_on_idle(ui, Color32::TRANSPARENT);
157   utils::no_border(ui);
158
159   ui.style_mut().visuals.widgets.inactive.expansion = 0.0;
160   ui.style_mut().visuals.widgets.hovered.expansion = 0.0;
161   ui.style_mut().visuals.widgets.active.expansion = 0.0;
162   ui.style_mut().visuals.widgets.inactive.corner_radius = CornerRadius::ZERO;
163   ui.style_mut().visuals.widgets.hovered.corner_radius = CornerRadius::ZERO;
164   ui.style_mut().visuals.widgets.active.corner_radius = CornerRadius::ZERO;
165
166   let button_size = vec2(45.0, window.bar_height);
167   let text_size = window.button_text_size;
168   let text_color = window.button_text_color;
169
170   let add_title_button = |ui: &mut Ui, text: &str, hover_color: Color32| -> bool {
171      ui.scope(|ui| {
172         utils::bg_color_on_hover(ui, hover_color);
173         let rich_text = RichText::new(text).color(text_color).size(text_size);
174         let button = Button::new(rich_text).min_size(button_size);
175
176         ui.add_sized(button_size, button).clicked()
177      })
178      .inner
179   };
180
181   // Close Button
182   if add_title_button(ui, "❌", window.close_on_hover_color) {
183      ui.ctx().send_viewport_cmd(ViewportCommand::Close);
184   }
185
186   let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
187
188   // Maximize/Restore
189   let max_icon = if is_maximized { "🗗" } else { "🗖" };
190   if add_title_button(ui, max_icon, window.on_hover_color) {
191      ui.ctx().send_viewport_cmd(ViewportCommand::Maximized(!is_maximized));
192   }
193
194   // Minimize
195   if add_title_button(ui, "🗕", window.on_hover_color) {
196      ui.ctx().send_viewport_cmd(ViewportCommand::Minimized(true));
197   }
198}