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