1use super::{Theme, utils};
2use egui::{
3 self, Button, CentralPanel, Color32, Rect, RichText, UiBuilder, ViewportCommand, vec2,
4};
5
6pub 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 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 painter.text(
55 title_bar_rect.center(),
56 Align2::CENTER_CENTER,
57 title,
58 FontId::proportional(title_size),
59 title_color,
60 );
61
62 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 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
94fn 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}