Skip to main content

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 {}