tab_factory/
tab_factory.rs1use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt};
2use relm4::{
3 ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent,
4 factory::{DynamicIndex, FactoryComponent, FactorySender, FactoryVecDeque},
5};
6
7#[derive(Debug)]
8struct Counter {
9 value: u8,
10}
11
12#[derive(Debug)]
13enum CounterMsg {
14 Increment,
15 Decrement,
16}
17
18#[derive(Debug)]
19enum CounterOutput {
20 SendFront(DynamicIndex),
21 MoveUp(DynamicIndex),
22 MoveDown(DynamicIndex),
23}
24
25#[relm4::factory]
26impl FactoryComponent for Counter {
27 type Init = u8;
28 type Input = CounterMsg;
29 type Output = CounterOutput;
30 type CommandOutput = ();
31 type ParentWidget = adw::TabView;
32
33 view! {
34 #[root]
35 gtk::Box {
36 set_orientation: gtk::Orientation::Horizontal,
37 set_spacing: 12,
38
39 gtk::Box {
40 set_spacing: 12,
41
42 #[name(label)]
43 gtk::Label {
44 #[watch]
45 set_label: &self.value.to_string(),
46 set_width_chars: 3,
47 },
48
49 gtk::Button {
50 set_label: "+",
51 connect_clicked => CounterMsg::Increment,
52 },
53
54 gtk::Button {
55 set_label: "-",
56 connect_clicked => CounterMsg::Decrement,
57 },
58
59 gtk::Button {
60 set_label: "Up",
61 connect_clicked[sender, index] => move |_| {
62 sender.output(CounterOutput::MoveUp(index.clone())).unwrap()
63 }
64 },
65
66 gtk::Button {
67 set_label: "Down",
68 connect_clicked[sender, index] => move |_| {
69 sender.output(CounterOutput::MoveDown(index.clone())).unwrap()
70 }
71 },
72
73 gtk::Button {
74 set_label: "To Start",
75 connect_clicked[sender, index] => move |_| {
76 sender.output(CounterOutput::SendFront(index.clone())).unwrap()
77 }
78 }
79 }
80 },
81 #[local_ref]
82 returned_widget -> adw::TabPage {
83 set_title: &format!("Page {}", self.value),
84 }
85 }
86
87 fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
88 Self { value }
89 }
90
91 fn update(&mut self, msg: Self::Input, _sender: FactorySender<Self>) {
92 match msg {
93 CounterMsg::Increment => {
94 self.value = self.value.wrapping_add(1);
95 }
96 CounterMsg::Decrement => {
97 self.value = self.value.wrapping_sub(1);
98 }
99 }
100 }
101}
102
103struct App {
104 created_widgets: u8,
105 counters: FactoryVecDeque<Counter>,
106}
107
108#[derive(Debug)]
109enum AppMsg {
110 AddCounter,
111 RemoveCounter,
112 SendFront(DynamicIndex),
113 MoveUp(DynamicIndex),
114 MoveDown(DynamicIndex),
115}
116
117#[relm4::component]
118impl SimpleComponent for App {
119 type Init = u8;
120 type Input = AppMsg;
121 type Output = ();
122
123 view! {
124 adw::Window {
125 set_title: Some("Tab factory example"),
126 set_default_size: (300, 100),
127
128 gtk::Box {
129 set_orientation: gtk::Orientation::Vertical,
130 set_spacing: 6,
131
132 adw::HeaderBar {},
133
134 adw::TabBar {
135 set_view: Some(tab_view),
136 set_autohide: false,
137 },
138
139 gtk::Button {
140 set_label: "Add counter",
141 connect_clicked => AppMsg::AddCounter,
142 },
143
144 gtk::Button {
145 set_label: "Remove counter",
146 connect_clicked => AppMsg::RemoveCounter,
147 },
148
149 #[local_ref]
150 tab_view -> adw::TabView {
151 set_margin_all: 6,
152 }
153 }
154 }
155 }
156
157 fn init(
158 counter: Self::Init,
159 root: Self::Root,
160 sender: ComponentSender<Self>,
161 ) -> ComponentParts<Self> {
162 let counters = FactoryVecDeque::builder()
163 .launch(adw::TabView::default())
164 .forward(sender.input_sender(), |output| match output {
165 CounterOutput::SendFront(index) => AppMsg::SendFront(index),
166 CounterOutput::MoveUp(index) => AppMsg::MoveUp(index),
167 CounterOutput::MoveDown(index) => AppMsg::MoveDown(index),
168 });
169 let model = App {
170 created_widgets: counter,
171 counters,
172 };
173
174 let tab_view = model.counters.widget();
175 let widgets = view_output!();
176
177 ComponentParts { model, widgets }
178 }
179
180 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
181 let mut counters_guard = self.counters.guard();
182
183 match msg {
184 AppMsg::AddCounter => {
185 counters_guard.push_back(self.created_widgets);
186 self.created_widgets = self.created_widgets.wrapping_add(1);
187 }
188 AppMsg::RemoveCounter => {
189 counters_guard.pop_back();
190 }
191 AppMsg::SendFront(index) => {
192 counters_guard.move_front(index.current_index());
193 }
194 AppMsg::MoveDown(index) => {
195 let index = index.current_index();
196 let new_index = index + 1;
197 if new_index < counters_guard.len() {
199 counters_guard.move_to(index, new_index);
200 }
201 }
202 AppMsg::MoveUp(index) => {
203 let index = index.current_index();
204 if index != 0 {
206 counters_guard.move_to(index, index - 1);
207 }
208 }
209 }
210 }
211}
212
213fn main() {
214 let app = RelmApp::new("relm4.example.tab_factory");
215 app.run::<App>(0);
216}