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