navigation_splitview_with_stack/
navigation_splitview_with_stack.rs1use relm4::RelmApp;
2
3fn main() {
4 let app = RelmApp::new("relm4.example.navigation_stack");
5 app.run::<app::App>((0, false));
6}
7
8mod app {
10 use crate::{counter::CounterModel, toggler::TogglerModel};
11 use adw::prelude::{AdwApplicationWindowExt, IsA, NavigationPageExt, ToValue};
12 use gtk::glib;
13 use relm4::{
14 Component, ComponentController, ComponentParts, ComponentSender, Controller,
15 SimpleComponent, adw,
16 };
17 use std::convert::identity;
18
19 pub struct App {
20 _counter: Controller<CounterModel>,
26 _toggler: Controller<TogglerModel>,
27 }
28
29 #[derive(Debug)]
30 pub enum Msg {}
31
32 #[relm4::component(pub)]
33 impl SimpleComponent for App {
34 type Init = (u8, bool);
35 type Input = Msg;
36 type Output = ();
37
38 view! {
39 #[root]
40 adw::ApplicationWindow {
41 #[name(split_view)]
42 adw::NavigationSplitView {
43 #[wrap(Some)]
44 set_sidebar = &adw::NavigationPage {
45 set_title: "Sidebar",
46
47 #[wrap(Some)]
48 set_child = &adw::ToolbarView {
49 add_top_bar = &adw::HeaderBar {},
50
51 #[wrap(Some)]
52 set_content = >k::StackSidebar {
53 set_stack: &stack,
54 },
55 },
56 },
57
58 #[wrap(Some)]
59 set_content = &adw::NavigationPage {
60 set_title: "Content",
61
62 #[wrap(Some)]
63 set_child = &adw::ToolbarView {
64 add_top_bar = &adw::HeaderBar {},
65 set_content: Some(&stack),
66 }
67 },
68 },
69
70 add_breakpoint = bp_with_setters(
71 adw::Breakpoint::new(
72 adw::BreakpointCondition::new_length(
73 adw::BreakpointConditionLengthType::MaxWidth,
74 400.0,
75 adw::LengthUnit::Sp,
76 )
77 ),
78 &[(&split_view, "collapsed", true)]
79 ),
80 },
81 stack = >k::Stack {
82 add_titled: (counter.widget(), None, "Counter"),
83 add_titled: (toggler.widget(), None, "Toggle"),
84 set_vhomogeneous: false,
85 }
86 }
87
88 fn init(
89 init: Self::Init,
90 root: Self::Root,
91 sender: ComponentSender<Self>,
92 ) -> ComponentParts<Self> {
93 let counter = CounterModel::builder()
94 .launch(init.0)
95 .forward(sender.input_sender(), identity);
96 let toggler = TogglerModel::builder()
97 .launch(init.1)
98 .forward(sender.input_sender(), identity);
99
100 let widgets = view_output!();
101
102 let model = App {
103 _counter: counter,
104 _toggler: toggler,
105 };
106
107 widgets.stack.connect_visible_child_notify({
108 let split_view = widgets.split_view.clone();
109 move |_| {
110 split_view.set_show_content(true);
111 }
112 });
113
114 ComponentParts { model, widgets }
115 }
116
117 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
118 match msg {}
119 }
120 }
121
122 fn bp_with_setters(
123 bp: adw::Breakpoint,
124 additions: &[(&impl IsA<glib::Object>, &str, impl ToValue)],
125 ) -> adw::Breakpoint {
126 bp.add_setters(additions);
127 bp
128 }
129}
130
131mod counter {
133 use crate::app::Msg;
134 use gtk::prelude::{BoxExt, ButtonExt, OrientableExt};
135 use relm4::{ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent};
136
137 pub struct CounterModel {
138 counter: u8,
139 }
140
141 #[derive(Debug)]
142 pub enum CounterMsg {
143 Increment,
144 Decrement,
145 }
146
147 #[relm4::component(pub)]
148 impl SimpleComponent for CounterModel {
149 type Init = u8;
150 type Input = CounterMsg;
151 type Output = Msg;
152
153 view! {
154 #[root]
155 gtk::Box {
156 set_orientation: gtk::Orientation::Vertical,
157 set_spacing: 5,
158 set_margin_all: 5,
159
160 gtk::Button {
161 set_label: "Increment",
162 connect_clicked => CounterMsg::Increment
163 },
164
165 gtk::Button::with_label("Decrement") {
166 connect_clicked => CounterMsg::Decrement
167 },
168
169 gtk::Label {
170 #[watch]
171 set_label: &format!("Counter: {}", model.counter),
172 set_margin_all: 5,
173 }
174 }
175 }
176
177 fn init(
178 init: Self::Init,
179 root: Self::Root,
180 sender: ComponentSender<Self>,
181 ) -> ComponentParts<Self> {
182 let model = CounterModel { counter: init };
183
184 let widgets = view_output!();
185
186 ComponentParts { model, widgets }
187 }
188
189 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
190 match msg {
191 CounterMsg::Increment => {
192 self.counter = self.counter.wrapping_add(1);
193 }
194 CounterMsg::Decrement => {
195 self.counter = self.counter.wrapping_sub(1);
196 }
197 }
198 }
199 }
200}
201
202mod toggler {
204 use crate::app::Msg;
205 use gtk::prelude::{BoxExt, ButtonExt, OrientableExt};
206 use relm4::{ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent};
207
208 pub struct TogglerModel {
209 toggle: bool,
210 }
211
212 #[derive(Debug)]
213 pub enum ToggleMsg {
214 Toggle,
215 }
216
217 #[relm4::component(pub)]
218 impl SimpleComponent for TogglerModel {
219 type Init = bool;
220 type Input = ToggleMsg;
221 type Output = Msg;
222
223 view! {
224 #[root]
225 gtk::Box {
226 set_orientation: gtk::Orientation::Vertical,
227 set_spacing: 5,
228 set_margin_all: 5,
229
230 gtk::ToggleButton {
231 set_label: "Toggle",
232 connect_clicked => ToggleMsg::Toggle,
233 },
234
235 gtk::Label {
236 #[watch]
237 set_label: &format!("Toggle: {}", model.toggle),
238 set_margin_all: 5,
239 }
240 }
241 }
242
243 fn init(
244 init: Self::Init,
245 root: Self::Root,
246 sender: ComponentSender<Self>,
247 ) -> ComponentParts<Self> {
248 let model = TogglerModel { toggle: init };
249
250 let widgets = view_output!();
251
252 ComponentParts { model, widgets }
253 }
254
255 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
256 match msg {
257 ToggleMsg::Toggle => {
258 self.toggle = !self.toggle;
259 }
260 }
261 }
262 }
263}