drop_sub_components/
drop_sub_components.rs

1use std::fmt::Debug;
2
3use relm4::RelmRemoveAllExt;
4use relm4::gtk::prelude::*;
5use relm4::prelude::*;
6
7mod css {
8    pub const COMPONENT_SPACING: i32 = 5;
9}
10
11enum AppMode {
12    Initial(#[allow(dead_code)] Controller<InitialScreen>),
13    SubScreen1(#[allow(dead_code)] Controller<SubScreen1>),
14    SubScreen2(#[allow(dead_code)] Controller<SubScreen2>),
15}
16
17struct App {
18    mode: Option<AppMode>,
19}
20
21#[derive(Debug)]
22enum Msg {
23    ShowInitialScreen,
24    ShowSubScreen1,
25    ShowSubScreen2,
26}
27
28#[relm4::component]
29impl Component for App {
30    type Init = ();
31    type Input = Msg;
32    type Output = ();
33    type CommandOutput = ();
34
35    view! {
36        gtk::Window {
37            set_title: Some("Drop sub components"),
38            set_decorated: false,
39
40            gtk::Box {
41                set_orientation: gtk::Orientation::Vertical,
42                gtk::HeaderBar {},
43                #[name="container"]
44                gtk::Box {
45                    set_orientation: gtk::Orientation::Vertical,
46                }
47            }
48        }
49    }
50
51    fn init(
52        _init: Self::Init,
53        root: Self::Root,
54        sender: ComponentSender<Self>,
55    ) -> ComponentParts<Self> {
56        let mut model = Self { mode: None };
57        let mut widgets = view_output!();
58        model.update_with_view(&mut widgets, Msg::ShowInitialScreen, sender, &root);
59        ComponentParts { model, widgets }
60    }
61
62    fn update_with_view(
63        &mut self,
64        widgets: &mut Self::Widgets,
65        message: Self::Input,
66        sender: ComponentSender<Self>,
67        root: &Self::Root,
68    ) {
69        widgets.container.remove_all();
70        match message {
71            Msg::ShowInitialScreen => {
72                let controller =
73                    InitialScreen::builder()
74                        .launch(())
75                        .forward(sender.input_sender(), |msg| match msg {
76                            InitialScreenOutput::ShowSubScreen1 => Msg::ShowSubScreen1,
77                            InitialScreenOutput::ShowSubScreen2 => Msg::ShowSubScreen2,
78                        });
79                widgets.container.append(controller.widget());
80                self.mode = Some(AppMode::Initial(controller));
81            }
82            Msg::ShowSubScreen1 => {
83                let controller = SubScreen1::builder()
84                    .launch(())
85                    .forward(sender.input_sender(), |_| Msg::ShowInitialScreen);
86                widgets.container.append(controller.widget());
87                self.mode = Some(AppMode::SubScreen1(controller));
88            }
89            Msg::ShowSubScreen2 => {
90                let controller = SubScreen2::builder()
91                    .launch(())
92                    .forward(sender.input_sender(), |_| Msg::ShowInitialScreen);
93                widgets.container.append(controller.widget());
94                self.mode = Some(AppMode::SubScreen2(controller));
95            }
96        }
97        root.set_default_size(400, 300);
98    }
99}
100
101struct InitialScreen {}
102
103#[derive(Debug)]
104enum InitialScreenOutput {
105    ShowSubScreen1,
106    ShowSubScreen2,
107}
108
109#[relm4::component]
110//noinspection RsSortImplTraitMembers
111impl SimpleComponent for InitialScreen {
112    type Input = ();
113    type Output = InitialScreenOutput;
114    type Init = ();
115
116    fn init(
117        _init: Self::Init,
118        root: Self::Root,
119        sender: ComponentSender<Self>,
120    ) -> ComponentParts<Self> {
121        let model = Self {};
122        let widgets = view_output!();
123        ComponentParts { model, widgets }
124    }
125
126    view! {
127        gtk::Box {
128            set_orientation: gtk::Orientation::Vertical,
129            set_hexpand: true,
130            set_vexpand: true,
131            set_halign: gtk::Align::Center,
132            set_valign: gtk::Align::Center,
133            gtk::Box {
134                set_orientation: gtk::Orientation::Vertical,
135                set_spacing: css::COMPONENT_SPACING,
136                gtk::Button {
137                    set_label: "Sub screen 1",
138                    connect_clicked[sender] => move |_| sender.output(InitialScreenOutput::ShowSubScreen1).unwrap()
139                },
140                gtk::Button {
141                    set_label: "Sub screen 2",
142                    connect_clicked[sender] => move |_| sender.output(InitialScreenOutput::ShowSubScreen2).unwrap()
143                },
144                gtk::Label {
145                    set_label: "Inspect console to see what happens\nwhen you enter and exit sub screens.\nThis allows for releasing unused resources\nif sub-screens hold heavy references.",
146                }
147            }
148        }
149    }
150}
151
152struct SubScreen1 {}
153
154impl Drop for SubScreen1 {
155    fn drop(&mut self) {
156        println!("Dropping SubScreen1");
157    }
158}
159
160#[relm4::component]
161//noinspection RsSortImplTraitMembers
162impl SimpleComponent for SubScreen1 {
163    type Input = ();
164    type Output = ();
165    type Init = ();
166
167    fn init(
168        _init: Self::Init,
169        root: Self::Root,
170        sender: ComponentSender<Self>,
171    ) -> ComponentParts<Self> {
172        println!("init SubScreen1");
173        let model = Self {};
174        let widgets = view_output!();
175        ComponentParts { model, widgets }
176    }
177
178    view! {
179        gtk::Box {
180            set_orientation: gtk::Orientation::Vertical,
181            set_width_request: 500,
182            gtk::Box {
183                set_orientation: gtk::Orientation::Vertical,
184                set_hexpand: true,
185                set_vexpand: true,
186                set_halign: gtk::Align::Center,
187                set_valign: gtk::Align::Center,
188
189                gtk::Label {
190                    set_label: "Sub screen 1",
191                },
192                gtk::Label {
193                    set_label: "Imagine this screen is not used often,\nbut holds reference to something\nthat consumes a lot of memory.",
194                },
195                gtk::Box {
196                    set_orientation: gtk::Orientation::Vertical,
197                    set_spacing: css::COMPONENT_SPACING,
198                    gtk::Button {
199                        set_label: "Back",
200                        connect_clicked[sender] => move |_| sender.output(()).unwrap()
201                    }
202                }
203            }
204        }
205    }
206}
207
208struct SubScreen2 {}
209
210impl Drop for SubScreen2 {
211    fn drop(&mut self) {
212        println!("Dropping SubScreen2");
213    }
214}
215
216#[relm4::component]
217//noinspection RsSortImplTraitMembers
218impl SimpleComponent for SubScreen2 {
219    type Input = ();
220    type Output = ();
221    type Init = ();
222
223    fn init(
224        _init: Self::Init,
225        root: Self::Root,
226        sender: ComponentSender<Self>,
227    ) -> ComponentParts<Self> {
228        println!("init SubScreen2");
229        let model = Self {};
230        let widgets = view_output!();
231        ComponentParts { model, widgets }
232    }
233
234    view! {
235        gtk::Box {
236            set_orientation: gtk::Orientation::Vertical,
237            set_height_request: 500,
238            gtk::Box {
239                set_orientation: gtk::Orientation::Vertical,
240                set_hexpand: true,
241                set_vexpand: true,
242                set_halign: gtk::Align::Center,
243                set_valign: gtk::Align::Center,
244
245                gtk::Label {
246                    set_label: "Sub screen 2",
247                },
248                gtk::Label {
249                    set_label: "Imagine this screen opens connection\nto remote host in order to show some live data.",
250                },
251                gtk::Box {
252                    set_orientation: gtk::Orientation::Vertical,
253                    set_spacing: css::COMPONENT_SPACING,
254                    gtk::Button {
255                        set_label: "Back",
256                        connect_clicked[sender] => move |_| sender.output(()).unwrap()
257                    }
258                }
259            }
260        }
261    }
262}
263
264fn main() {
265    let app = RelmApp::new("relm4.example.drop_sub_components");
266    app.run::<App>(());
267}