gtk/dialog.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::DialogFlags;
4use crate::ResponseType;
5use crate::Widget;
6use crate::Window;
7use crate::prelude::*;
8use crate::{Dialog, ffi};
9use glib::translate::*;
10use std::cell::Cell;
11use std::future::Future;
12use std::pin::Pin;
13use std::ptr;
14
15impl Dialog {
16 ///
17 /// GtkWidget *main_app_window; // Window the dialog should show up on
18 /// GtkWidget *dialog;
19 /// GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
20 /// dialog = gtk_dialog_new_with_buttons ("My dialog",
21 /// main_app_window,
22 /// flags,
23 /// _("_OK"),
24 /// GTK_RESPONSE_ACCEPT,
25 /// _("_Cancel"),
26 /// GTK_RESPONSE_REJECT,
27 /// NULL);
28 /// ]|
29 /// ## `title`
30 /// Title of the dialog, or [`None`]
31 /// ## `parent`
32 /// Transient parent of the dialog, or [`None`]
33 /// ## `flags`
34 /// from [`DialogFlags`][crate::DialogFlags]
35 /// ## `first_button_text`
36 /// text to go in first button, or [`None`]
37 ///
38 /// # Returns
39 ///
40 /// a new [`Dialog`][crate::Dialog]
41 #[doc(alias = "gtk_dialog_new_with_buttons")]
42 pub fn with_buttons<T: IsA<Window>>(
43 title: Option<&str>,
44 parent: Option<&T>,
45 flags: DialogFlags,
46 buttons: &[(&str, ResponseType)],
47 ) -> Dialog {
48 assert_initialized_main_thread!();
49 let ret: Dialog = unsafe {
50 Widget::from_glib_none(ffi::gtk_dialog_new_with_buttons(
51 title.to_glib_none().0,
52 parent.map(|p| p.as_ref()).to_glib_none().0,
53 flags.into_glib(),
54 ptr::null_mut(),
55 ))
56 .unsafe_cast()
57 };
58
59 ret.add_buttons(buttons);
60 ret
61 }
62}
63
64mod sealed {
65 pub trait Sealed {}
66 impl<T: glib::object::IsA<crate::Dialog> + glib::object::IsA<crate::Widget>> Sealed for T {}
67}
68
69pub trait DialogExtManual: IsA<Dialog> + IsA<Widget> + sealed::Sealed + 'static {
70 /// Adds more buttons, same as calling [`DialogExt::add_button()`][crate::prelude::DialogExt::add_button()]
71 /// repeatedly. The variable argument list should be [`None`]-terminated
72 /// as with `gtk_dialog_new_with_buttons()`. Each button must have both
73 /// text and response ID.
74 /// ## `first_button_text`
75 /// button text
76 #[doc(alias = "gtk_dialog_add_buttons")]
77 fn add_buttons(&self, buttons: &[(&str, ResponseType)]) {
78 for &(text, id) in buttons {
79 //FIXME: self.add_button don't work on 1.8
80 Self::add_button(self, text, id);
81 }
82 }
83
84 // rustdoc-stripper-ignore-next
85 /// Shows the dialog and returns a `Future` that resolves to the
86 /// `ResponseType` on response.
87 ///
88 /// ```no_run
89 /// use gtk::prelude::*;
90 ///
91 /// # async fn run() {
92 /// let dialog = gtk::MessageDialog::builder()
93 /// .buttons(gtk::ButtonsType::OkCancel)
94 /// .text("What is your answer?")
95 /// .build();
96 ///
97 /// let answer = dialog.run_future().await;
98 /// dialog.close();
99 /// println!("Answer: {:?}", answer);
100 /// # }
101 /// ```
102 fn run_future<'a>(&'a self) -> Pin<Box<dyn Future<Output = ResponseType> + 'a>> {
103 Box::pin(async move {
104 let (sender, receiver) = futures_channel::oneshot::channel();
105
106 let sender = Cell::new(Some(sender));
107
108 let response_handler = self.connect_response(move |_, response_type| {
109 if let Some(m) = sender.replace(None) {
110 let _result = m.send(response_type);
111 }
112 });
113
114 self.show();
115
116 if let Ok(response) = receiver.await {
117 if response != ResponseType::DeleteEvent {
118 self.disconnect(response_handler);
119 }
120 response
121 } else {
122 ResponseType::None
123 }
124 })
125 }
126}
127
128impl<O: IsA<Dialog> + IsA<Widget>> DialogExtManual for O {}