transient_dialog/
transient_dialog.rs

1// Don't show GTK 4.10 deprecations.
2// We can't replace them without raising the GTK requirement to 4.10.
3#![allow(deprecated)]
4
5use std::convert::identity;
6
7use gtk::glib;
8use gtk::prelude::{ButtonExt, GtkWindowExt, WidgetExt};
9use relm4::MessageBroker;
10use relm4::{
11    Component, ComponentController, ComponentParts, ComponentSender, Controller, RelmApp,
12    SimpleComponent,
13};
14
15static DIALOG_BROKER: MessageBroker<DialogMsg> = MessageBroker::new();
16
17struct Dialog {
18    visible: bool,
19}
20
21#[derive(Debug)]
22enum DialogMsg {
23    Show,
24    Hide,
25}
26
27#[relm4::component]
28impl SimpleComponent for Dialog {
29    type Init = ();
30    type Input = DialogMsg;
31    type Output = ButtonMsg;
32
33    view! {
34        dialog = gtk::Dialog {
35            #[watch]
36            set_visible: model.visible,
37            set_modal: true,
38
39            #[wrap(Some)]
40            set_child = &gtk::Label {
41                set_width_request: 200,
42                set_height_request: 80,
43                set_halign: gtk::Align::Center,
44                set_valign: gtk::Align::Center,
45                #[watch]
46                set_label: if dialog.transient_for().is_some() {
47                    "I'm transient!"
48                } else {
49                    "I'm not transient..."
50                },
51            },
52
53            connect_close_request[sender] => move |_| {
54                sender.input(DialogMsg::Hide);
55                glib::Propagation::Stop
56            }
57        }
58    }
59
60    fn init(
61        _init: Self::Init,
62        root: Self::Root,
63        sender: ComponentSender<Self>,
64    ) -> ComponentParts<Self> {
65        let model = Dialog { visible: false };
66        let widgets = view_output!();
67        ComponentParts { model, widgets }
68    }
69
70    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
71        match msg {
72            DialogMsg::Show => self.visible = true,
73            DialogMsg::Hide => self.visible = false,
74        }
75    }
76}
77
78struct Button {
79    #[allow(dead_code)]
80    dialog: Controller<Dialog>,
81}
82
83#[derive(Debug)]
84enum ButtonMsg {}
85
86#[relm4::component]
87impl SimpleComponent for Button {
88    type Init = ();
89    type Input = ButtonMsg;
90    type Output = AppMsg;
91
92    view! {
93        button = &gtk::Button {
94            set_label: "Show the dialog",
95            connect_clicked => move |_| {
96                DIALOG_BROKER.send(DialogMsg::Show);
97            }
98        }
99    }
100
101    fn init(
102        _init: Self::Init,
103        root: Self::Root,
104        sender: ComponentSender<Self>,
105    ) -> ComponentParts<Self> {
106        // We don't have access to the parent window from here
107        // but we can just use the button to set the transient window for the dialog.
108        // Relm4 will get the window later by calling [`WidgetExt::root()`]
109        // on the button once all widgets are connected.
110        let dialog = Dialog::builder()
111            .transient_for(&root)
112            .launch_with_broker((), &DIALOG_BROKER)
113            .forward(sender.input_sender(), identity);
114
115        let model = Button { dialog };
116        let widgets = view_output!();
117        ComponentParts { model, widgets }
118    }
119
120    fn update(&mut self, _msg: Self::Input, _sender: ComponentSender<Self>) {}
121}
122
123#[derive(Debug)]
124enum AppMsg {}
125
126struct App {
127    button: Controller<Button>,
128}
129
130#[relm4::component]
131impl SimpleComponent for App {
132    type Init = ();
133    type Input = AppMsg;
134    type Output = ();
135
136    view! {
137        main_window = gtk::ApplicationWindow {
138            set_default_size: (500, 250),
139            set_child: Some(model.button.widget()),
140        }
141    }
142
143    fn init(
144        _init: Self::Init,
145        root: Self::Root,
146        sender: ComponentSender<Self>,
147    ) -> ComponentParts<Self> {
148        let button = Button::builder()
149            .launch(())
150            .forward(sender.input_sender(), identity);
151        let model = App { button };
152        let widgets = view_output!();
153        ComponentParts { model, widgets }
154    }
155
156    fn update(&mut self, _msg: Self::Input, _sender: ComponentSender<Self>) {}
157}
158
159fn main() {
160    let app = RelmApp::new("relm4.example.transient_dialog");
161    app.run::<App>(());
162}