1use crate::{
2 button::{ButtonBuilder, ButtonMessage},
3 core::{algebra::Vector2, pool::Handle},
4 define_constructor,
5 draw::DrawingContext,
6 formatted_text::WrapMode,
7 grid::{Column, GridBuilder, Row},
8 message::{MessageDirection, OsEvent, UiMessage},
9 stack_panel::StackPanelBuilder,
10 text::{TextBuilder, TextMessage},
11 widget::{Widget, WidgetBuilder},
12 window::{Window, WindowBuilder, WindowMessage, WindowTitle},
13 BuildContext, Control, HorizontalAlignment, NodeHandleMapping, Orientation, RestrictionEntry,
14 Thickness, UiNode, UserInterface,
15};
16use std::{
17 any::{Any, TypeId},
18 ops::{Deref, DerefMut},
19 sync::mpsc::Sender,
20};
21
22#[derive(Debug, Clone, PartialEq)]
23pub enum MessageBoxMessage {
24 Open {
25 title: Option<String>,
26 text: Option<String>,
27 },
28 Close(MessageBoxResult),
29}
30
31impl MessageBoxMessage {
32 define_constructor!(MessageBoxMessage:Open => fn open(title: Option<String>, text: Option<String>), layout: false);
33 define_constructor!(MessageBoxMessage:Close => fn close(MessageBoxResult), layout: false);
34}
35
36#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Debug)]
37pub enum MessageBoxResult {
38 Ok,
39 No,
40 Yes,
41 Cancel,
42}
43
44#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Debug)]
45pub enum MessageBoxButtons {
46 Ok,
47 YesNo,
48 YesNoCancel,
49}
50
51#[derive(Clone)]
52pub struct MessageBox {
53 window: Window,
54 buttons: MessageBoxButtons,
55 ok_yes: Handle<UiNode>,
56 no: Handle<UiNode>,
57 cancel: Handle<UiNode>,
58 text: Handle<UiNode>,
59}
60
61impl Deref for MessageBox {
62 type Target = Widget;
63
64 fn deref(&self) -> &Self::Target {
65 &self.window
66 }
67}
68
69impl DerefMut for MessageBox {
70 fn deref_mut(&mut self) -> &mut Self::Target {
71 &mut self.window
72 }
73}
74
75impl Control for MessageBox {
78 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
79 self.window.query_component(type_id).or_else(|| {
80 if type_id == TypeId::of::<Self>() {
81 Some(self)
82 } else {
83 None
84 }
85 })
86 }
87
88 fn resolve(&mut self, node_map: &NodeHandleMapping) {
89 self.window.resolve(node_map);
90 node_map.resolve(&mut self.ok_yes);
91 node_map.resolve(&mut self.no);
92 node_map.resolve(&mut self.cancel);
93 node_map.resolve(&mut self.text);
94 }
95
96 fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
97 self.window.measure_override(ui, available_size)
98 }
99
100 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
101 self.window.arrange_override(ui, final_size)
102 }
103
104 fn draw(&self, drawing_context: &mut DrawingContext) {
105 self.window.draw(drawing_context)
106 }
107
108 fn update(&mut self, dt: f32, sender: &Sender<UiMessage>) {
109 self.window.update(dt, sender);
110 }
111
112 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
113 self.window.handle_routed_message(ui, message);
114
115 if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
116 if message.destination() == self.ok_yes {
117 let result = match self.buttons {
118 MessageBoxButtons::Ok => MessageBoxResult::Ok,
119 MessageBoxButtons::YesNo => MessageBoxResult::Yes,
120 MessageBoxButtons::YesNoCancel => MessageBoxResult::Yes,
121 };
122 ui.send_message(MessageBoxMessage::close(
123 self.handle,
124 MessageDirection::ToWidget,
125 result,
126 ));
127 } else if message.destination() == self.cancel {
128 ui.send_message(MessageBoxMessage::close(
129 self.handle(),
130 MessageDirection::ToWidget,
131 MessageBoxResult::Cancel,
132 ));
133 } else if message.destination() == self.no {
134 ui.send_message(MessageBoxMessage::close(
135 self.handle(),
136 MessageDirection::ToWidget,
137 MessageBoxResult::No,
138 ));
139 }
140 } else if let Some(msg) = message.data::<MessageBoxMessage>() {
141 match msg {
142 MessageBoxMessage::Open { title, text } => {
143 if let Some(title) = title {
144 ui.send_message(WindowMessage::title(
145 self.handle(),
146 MessageDirection::ToWidget,
147 WindowTitle::Text(title.clone()),
148 ));
149 }
150
151 if let Some(text) = text {
152 ui.send_message(TextMessage::text(
153 self.text,
154 MessageDirection::ToWidget,
155 text.clone(),
156 ));
157 }
158
159 ui.send_message(WindowMessage::open_modal(
160 self.handle(),
161 MessageDirection::ToWidget,
162 true,
163 ));
164 }
165 MessageBoxMessage::Close(_) => {
166 ui.send_message(WindowMessage::close(
168 self.handle(),
169 MessageDirection::ToWidget,
170 ));
171 }
172 }
173 }
174 }
175
176 fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
177 self.window.preview_message(ui, message);
178 }
179
180 fn handle_os_event(
181 &mut self,
182 self_handle: Handle<UiNode>,
183 ui: &mut UserInterface,
184 event: &OsEvent,
185 ) {
186 self.window.handle_os_event(self_handle, ui, event);
187 }
188}
189
190pub struct MessageBoxBuilder<'b> {
191 window_builder: WindowBuilder,
192 buttons: MessageBoxButtons,
193 text: &'b str,
194}
195
196impl<'a, 'b> MessageBoxBuilder<'b> {
197 pub fn new(window_builder: WindowBuilder) -> Self {
198 Self {
199 window_builder,
200 buttons: MessageBoxButtons::Ok,
201 text: "",
202 }
203 }
204
205 pub fn with_text(mut self, text: &'b str) -> Self {
206 self.text = text;
207 self
208 }
209
210 pub fn with_buttons(mut self, buttons: MessageBoxButtons) -> Self {
211 self.buttons = buttons;
212 self
213 }
214
215 pub fn build(mut self, ctx: &mut BuildContext) -> Handle<UiNode> {
216 let ok_yes;
217 let mut no = Default::default();
218 let mut cancel = Default::default();
219 let text;
220 let content = match self.buttons {
221 MessageBoxButtons::Ok => GridBuilder::new(
222 WidgetBuilder::new()
223 .with_child({
224 text = TextBuilder::new(
225 WidgetBuilder::new().with_margin(Thickness::uniform(4.0)),
226 )
227 .with_text(self.text)
228 .with_wrap(WrapMode::Word)
229 .build(ctx);
230 text
231 })
232 .with_child({
233 ok_yes = ButtonBuilder::new(
234 WidgetBuilder::new()
235 .with_margin(Thickness::uniform(1.0))
236 .with_width(80.0)
237 .on_row(1)
238 .with_horizontal_alignment(HorizontalAlignment::Center),
239 )
240 .with_text("OK")
241 .build(ctx);
242 ok_yes
243 }),
244 )
245 .add_row(Row::stretch())
246 .add_row(Row::strict(25.0))
247 .add_column(Column::stretch())
248 .build(ctx),
249 MessageBoxButtons::YesNo => GridBuilder::new(
250 WidgetBuilder::new()
251 .with_child({
252 text = TextBuilder::new(WidgetBuilder::new())
253 .with_text(self.text)
254 .with_wrap(WrapMode::Word)
255 .build(ctx);
256 text
257 })
258 .with_child(
259 StackPanelBuilder::new(
260 WidgetBuilder::new()
261 .with_horizontal_alignment(HorizontalAlignment::Right)
262 .on_row(1)
263 .with_child({
264 ok_yes = ButtonBuilder::new(
265 WidgetBuilder::new()
266 .with_width(80.0)
267 .with_margin(Thickness::uniform(1.0)),
268 )
269 .with_text("Yes")
270 .build(ctx);
271 ok_yes
272 })
273 .with_child({
274 no = ButtonBuilder::new(
275 WidgetBuilder::new()
276 .with_width(80.0)
277 .with_margin(Thickness::uniform(1.0)),
278 )
279 .with_text("No")
280 .build(ctx);
281 no
282 }),
283 )
284 .with_orientation(Orientation::Horizontal)
285 .build(ctx),
286 ),
287 )
288 .add_row(Row::stretch())
289 .add_row(Row::strict(25.0))
290 .add_column(Column::stretch())
291 .build(ctx),
292 MessageBoxButtons::YesNoCancel => GridBuilder::new(
293 WidgetBuilder::new()
294 .with_child({
295 text = TextBuilder::new(WidgetBuilder::new())
296 .with_text(self.text)
297 .with_wrap(WrapMode::Word)
298 .build(ctx);
299 text
300 })
301 .with_child(
302 StackPanelBuilder::new(
303 WidgetBuilder::new()
304 .with_horizontal_alignment(HorizontalAlignment::Right)
305 .on_row(1)
306 .with_child({
307 ok_yes = ButtonBuilder::new(
308 WidgetBuilder::new()
309 .with_width(80.0)
310 .with_margin(Thickness::uniform(1.0)),
311 )
312 .with_text("Yes")
313 .build(ctx);
314 ok_yes
315 })
316 .with_child({
317 no = ButtonBuilder::new(
318 WidgetBuilder::new()
319 .with_width(80.0)
320 .with_margin(Thickness::uniform(1.0)),
321 )
322 .with_text("No")
323 .build(ctx);
324 no
325 })
326 .with_child({
327 cancel = ButtonBuilder::new(
328 WidgetBuilder::new()
329 .with_width(80.0)
330 .with_margin(Thickness::uniform(1.0)),
331 )
332 .with_text("Cancel")
333 .build(ctx);
334 cancel
335 }),
336 )
337 .with_orientation(Orientation::Horizontal)
338 .build(ctx),
339 ),
340 )
341 .add_row(Row::stretch())
342 .add_row(Row::strict(25.0))
343 .add_column(Column::stretch())
344 .build(ctx),
345 };
346
347 if self.window_builder.widget_builder.min_size.is_none() {
348 self.window_builder.widget_builder.min_size = Some(Vector2::new(200.0, 100.0));
349 }
350
351 self.window_builder.widget_builder.handle_os_events = true;
352
353 let is_open = self.window_builder.open;
354
355 let message_box = MessageBox {
356 buttons: self.buttons,
357 window: self.window_builder.with_content(content).build_window(ctx),
358 ok_yes,
359 no,
360 cancel,
361 text,
362 };
363
364 let handle = ctx.add_node(UiNode::new(message_box));
365
366 if is_open {
367 ctx.ui
369 .push_picking_restriction(RestrictionEntry { handle, stop: true });
370 }
371
372 handle
373 }
374}