drop_sub_components/
drop_sub_components.rs1use 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]
110impl 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]
161impl 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]
217impl 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}