gtk4/dialog.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{
4 cell::{Cell, RefCell},
5 future::Future,
6 pin::Pin,
7 ptr,
8 rc::Rc,
9};
10
11use glib::translate::*;
12
13use crate::{Dialog, DialogFlags, ResponseType, Widget, Window, ffi, prelude::*};
14
15impl Dialog {
16 /// s a simple example:
17 /// **⚠️ The following code is in c ⚠️**
18 ///
19 /// ```c
20 /// GtkWindow *main_app_window; // Window the dialog should show up on
21 /// GtkWidget *dialog;
22 /// GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
23 /// dialog = gtk_dialog_new_with_buttons ("My dialog",
24 /// main_app_window,
25 /// flags,
26 /// _("_OK"),
27 /// GTK_RESPONSE_ACCEPT,
28 /// _("_Cancel"),
29 /// GTK_RESPONSE_REJECT,
30 /// NULL);
31 /// ```
32 ///
33 /// # Deprecated since 4.10
34 ///
35 /// Use [`Window`][crate::Window] instead
36 /// ## `title`
37 /// Title of the dialog
38 /// ## `parent`
39 /// Transient parent of the dialog
40 /// ## `flags`
41 /// from [`DialogFlags`][crate::DialogFlags]
42 /// ## `first_button_text`
43 /// text to go in first button
44 ///
45 /// # Returns
46 ///
47 /// a new [`Dialog`][crate::Dialog]
48 #[doc(alias = "gtk_dialog_new_with_buttons")]
49 #[doc(alias = "new_with_buttons")]
50 #[cfg_attr(feature = "v4_10", deprecated = "Since 4.10")]
51 #[allow(deprecated)]
52 pub fn with_buttons<T: IsA<Window>>(
53 title: impl IntoOptionalGStr,
54 parent: Option<&T>,
55 flags: DialogFlags,
56 buttons: &[(&str, ResponseType)],
57 ) -> Self {
58 assert_initialized_main_thread!();
59 let ret: Self = unsafe {
60 title.run_with_gstr(|title| {
61 Widget::from_glib_none(ffi::gtk_dialog_new_with_buttons(
62 title.to_glib_none().0,
63 parent.map(|p| p.as_ref()).to_glib_none().0,
64 flags.into_glib(),
65 ptr::null_mut(),
66 ))
67 .unsafe_cast()
68 })
69 };
70
71 ret.add_buttons(buttons);
72 ret
73 }
74}
75
76// rustdoc-stripper-ignore-next
77/// Trait containing manually implemented methods of [`Dialog`](crate::Dialog).
78#[cfg_attr(feature = "v4_10", deprecated = "Since 4.10")]
79#[allow(deprecated)]
80pub trait DialogExtManual: IsA<Dialog> + 'static {
81 /// Adds multiple buttons.
82 ///
83 /// This is the same as calling [`DialogExt::add_button()`][crate::prelude::DialogExt::add_button()]
84 /// repeatedly. The variable argument list should be [`None`]-terminated
85 /// as with [`Dialog::with_buttons()`][crate::Dialog::with_buttons()]. Each button must have both
86 /// text and response ID.
87 ///
88 /// # Deprecated since 4.10
89 ///
90 /// Use [`Window`][crate::Window] instead
91 /// ## `first_button_text`
92 /// button text
93 #[doc(alias = "gtk_dialog_add_buttons")]
94 fn add_buttons(&self, buttons: &[(&str, ResponseType)]) {
95 for &(text, id) in buttons {
96 Self::add_button(self, text, id);
97 }
98 }
99
100 /// Gets the response id of a widget in the action area
101 /// of a dialog.
102 ///
103 /// # Deprecated since 4.10
104 ///
105 /// Use [`Window`][crate::Window] instead
106 /// ## `widget`
107 /// a widget in the action area of @self
108 ///
109 /// # Returns
110 ///
111 /// t have a response id set.
112 #[doc(alias = "gtk_dialog_get_response_for_widget")]
113 #[doc(alias = "get_response_for_widget")]
114 fn response_for_widget<P: IsA<Widget>>(&self, widget: &P) -> ResponseType {
115 unsafe {
116 from_glib(ffi::gtk_dialog_get_response_for_widget(
117 AsRef::<Dialog>::as_ref(self).to_glib_none().0,
118 widget.as_ref().to_glib_none().0,
119 ))
120 }
121 }
122
123 // rustdoc-stripper-ignore-next
124 /// Shows the dialog and returns a `Future` that resolves to the
125 /// `ResponseType` on response.
126 ///
127 /// ```no_run
128 /// use gtk4::prelude::*;
129 ///
130 /// # async fn run() {
131 /// let dialog = gtk4::MessageDialog::builder()
132 /// .buttons(gtk4::ButtonsType::OkCancel)
133 /// .text("What is your answer?")
134 /// .build();
135 ///
136 /// let answer = dialog.run_future().await;
137 /// dialog.close();
138 /// println!("Answer: {:?}", answer);
139 /// # }
140 /// ```
141 fn run_future<'a>(&'a self) -> Pin<Box<dyn Future<Output = ResponseType> + 'a>> {
142 Box::pin(async move {
143 let (sender, receiver) = futures_channel::oneshot::channel();
144
145 let sender = Cell::new(Some(sender));
146
147 let response_handler = self.connect_response(move |_, response_type| {
148 if let Some(m) = sender.replace(None) {
149 let _result = m.send(response_type);
150 }
151 });
152
153 self.as_ref().present();
154
155 if let Ok(response) = receiver.await {
156 self.disconnect(response_handler);
157 response
158 } else {
159 ResponseType::None
160 }
161 })
162 }
163
164 // rustdoc-stripper-ignore-next
165 /// Shows the dialog and calls the callback when a response has been
166 /// received.
167 ///
168 /// **Important**: this function isn't blocking.
169 ///
170 /// ```no_run
171 /// use gtk4::prelude::*;
172 ///
173 /// let dialog = gtk4::MessageDialog::builder()
174 /// .buttons(gtk4::ButtonsType::OkCancel)
175 /// .text("What is your answer?")
176 /// .build();
177 ///
178 /// dialog.run_async(|obj, answer| {
179 /// obj.close();
180 /// println!("Answer: {:?}", answer);
181 /// });
182 /// ```
183 fn run_async<F: FnOnce(&Self, ResponseType) + 'static>(&self, f: F) {
184 let response_handler = Rc::new(RefCell::new(None));
185 let response_handler_clone = response_handler.clone();
186 let f = RefCell::new(Some(f));
187 *response_handler.borrow_mut() = Some(self.connect_response(move |s, response_type| {
188 if let Some(handler) = response_handler_clone.borrow_mut().take() {
189 s.disconnect(handler);
190 }
191 (*f.borrow_mut()).take().expect("cannot get callback")(s, response_type);
192 }));
193 self.as_ref().present();
194 }
195}
196
197impl<O: IsA<Dialog>> DialogExtManual for O {}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use crate as gtk4;
203
204 #[test]
205 async fn dialog_future() {
206 let dialog = Dialog::new();
207 glib::idle_add_local_once(glib::clone!(
208 #[strong]
209 dialog,
210 move || {
211 dialog.response(ResponseType::Ok);
212 }
213 ));
214 let response = dialog.run_future().await;
215 assert_eq!(response, ResponseType::Ok);
216 }
217}