Skip to content
This repository was archived by the owner on Mar 4, 2024. It is now read-only.

Commit 2418edf

Browse files
authored
Merge pull request #268 from sophie-h/run-future
Add Dialog::run_future()
2 parents 1a4e844 + ad20f81 commit 2418edf

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

examples/src/bin/dialog.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//! # Dialog Example
2+
//!
3+
//! Example of how to obtain the response to a dialog as a future
4+
5+
use gtk::glib::clone;
6+
use gtk::prelude::*;
7+
8+
use std::env::args;
9+
use std::rc::Rc;
10+
11+
async fn dialog<W: IsA<gtk::Window>>(window: Rc<W>) {
12+
let question_dialog = gtk::MessageDialogBuilder::new()
13+
.transient_for(&*window)
14+
.modal(true)
15+
.buttons(gtk::ButtonsType::OkCancel)
16+
.text("What is your answer?")
17+
.build();
18+
19+
let answer = question_dialog.run_future().await;
20+
question_dialog.close();
21+
question_dialog.hide();
22+
23+
let info_dialog = gtk::MessageDialogBuilder::new()
24+
.transient_for(&*window)
25+
.modal(true)
26+
.buttons(gtk::ButtonsType::Close)
27+
.text("You answered")
28+
.secondary_text(&format!("Your answer: {:?}", answer))
29+
.build();
30+
31+
info_dialog.run_future().await;
32+
info_dialog.close();
33+
}
34+
35+
fn build_ui(application: &gtk::Application) {
36+
let button = gtk::ButtonBuilder::new()
37+
.label("Open Dialog")
38+
.halign(gtk::Align::Center)
39+
.valign(gtk::Align::Center)
40+
.visible(true)
41+
.build();
42+
43+
let window = Rc::new(
44+
gtk::ApplicationWindowBuilder::new()
45+
.application(application)
46+
.title("Dialog Example")
47+
.default_width(350)
48+
.default_height(70)
49+
.child(&button)
50+
.visible(true)
51+
.build(),
52+
);
53+
54+
button.connect_clicked(clone!(@strong window =>
55+
move |_| {
56+
gtk::glib::MainContext::default().spawn_local(dialog(Rc::clone(&window)));
57+
}
58+
));
59+
}
60+
61+
fn main() {
62+
let application = gtk::ApplicationBuilder::new()
63+
.application_id("com.github.gtk-rs.examples.dialog")
64+
.build();
65+
66+
application.connect_activate(build_ui);
67+
68+
application.run(&args().collect::<Vec<_>>());
69+
}

gtk/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ git = "https://github.com/gtk-rs/lgpl-docs"
5454
libc = "0.2"
5555
bitflags = "1.0"
5656
field-offset = "0.3"
57+
futures-channel = "0.3"
5758
once_cell = "1.0"
5859
atk = { path = "../atk" }
5960
ffi = { package = "gtk-sys", path = "sys" }

gtk/src/dialog.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ use crate::Dialog;
55
use crate::DialogFlags;
66
use crate::ResponseType;
77
use crate::Widget;
8+
use crate::WidgetExt;
89
use crate::Window;
910
use glib::object::Cast;
1011
use glib::translate::*;
1112
use glib::IsA;
13+
use glib::ObjectExt;
14+
use std::cell::Cell;
15+
use std::future::Future;
16+
use std::pin::Pin;
1217
use std::ptr;
1318

1419
impl Dialog {
@@ -38,13 +43,58 @@ impl Dialog {
3843
pub trait DialogExtManual: 'static {
3944
#[doc(alias = "gtk_dialog_add_buttons")]
4045
fn add_buttons(&self, buttons: &[(&str, ResponseType)]);
46+
47+
// rustdoc-stripper-ignore-next
48+
/// Shows the dialog and returns a `Future` that resolves to the
49+
/// `ResponseType` on response.
50+
///
51+
/// ```no_run
52+
/// use gtk::prelude::*;
53+
///
54+
/// # async fn run() {
55+
/// let dialog = gtk::MessageDialogBuilder::new()
56+
/// .buttons(gtk::ButtonsType::OkCancel)
57+
/// .text("What is your answer?")
58+
/// .build();
59+
///
60+
/// let answer = dialog.run_future().await;
61+
/// dialog.close();
62+
/// println!("Answer: {:?}", answer);
63+
/// # }
64+
/// ```
65+
fn run_future<'a>(&'a self) -> Pin<Box<dyn Future<Output = ResponseType> + 'a>>;
4166
}
4267

43-
impl<O: IsA<Dialog>> DialogExtManual for O {
68+
impl<O: IsA<Dialog> + IsA<Widget>> DialogExtManual for O {
4469
fn add_buttons(&self, buttons: &[(&str, ResponseType)]) {
4570
for &(text, id) in buttons {
4671
//FIXME: self.add_button don't work on 1.8
4772
O::add_button(self, text, id);
4873
}
4974
}
75+
76+
fn run_future<'a>(&'a self) -> Pin<Box<dyn Future<Output = ResponseType> + 'a>> {
77+
Box::pin(async move {
78+
let (sender, receiver) = futures_channel::oneshot::channel();
79+
80+
let sender = Cell::new(Some(sender));
81+
82+
let response_handler = self.connect_response(move |_, response_type| {
83+
if let Some(m) = sender.replace(None) {
84+
let _result = m.send(response_type);
85+
}
86+
});
87+
88+
self.show();
89+
90+
if let Ok(response) = receiver.await {
91+
if response != ResponseType::DeleteEvent {
92+
self.disconnect(response_handler);
93+
}
94+
response
95+
} else {
96+
ResponseType::None
97+
}
98+
})
99+
}
50100
}

0 commit comments

Comments
 (0)