factory_hash_map/
factory_hash_map.rs1use gtk::prelude::{
2 BoxExt, ButtonExt, EntryBufferExtManual, EntryExt, GtkWindowExt, OrientableExt, WidgetExt,
3};
4use relm4::factory::{FactoryComponent, FactoryHashMap, FactorySender};
5use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
6
7#[derive(Debug)]
8struct Counter {
9 name: String,
10 value: u8,
11}
12
13#[derive(Debug)]
14enum CounterOutput {}
15
16#[relm4::factory]
17impl FactoryComponent for Counter {
18 type Init = u8;
19 type Input = ();
20 type Output = CounterOutput;
21 type CommandOutput = ();
22 type ParentWidget = gtk::Stack;
23 type Index = String;
24
25 view! {
26 #[root]
27 root = gtk::Box {
28 set_orientation: gtk::Orientation::Horizontal,
29 set_halign: gtk::Align::Center,
30 set_spacing: 10,
31 set_margin_all: 12,
32
33 #[name(label)]
34 gtk::Label {
35 set_use_markup: true,
36 #[watch]
37 set_label: &format!("<b>Counter value: {}</b>", self.value),
38 set_width_chars: 3,
39 },
40
41 },
42 #[local_ref]
43 returned_widget -> gtk::StackPage {
44 set_name: &self.name,
45 set_title: &self.name,
46 }
47 }
48
49 fn init_model(value: Self::Init, index: &Self::Index, _sender: FactorySender<Self>) -> Self {
50 Self {
51 name: index.clone(),
52 value,
53 }
54 }
55
56 fn shutdown(&mut self, _widgets: &mut Self::Widgets, _output: relm4::Sender<Self::Output>) {
57 println!("Counter with value {} was destroyed", self.value);
58 }
59}
60
61struct App {
62 created_widgets: u8,
63 counters: FactoryHashMap<String, Counter>,
64 entry_buffer: gtk::EntryBuffer,
65}
66
67#[derive(Debug)]
68enum AppMsg {
69 UpdateView,
70 AddCounter,
71 Increment(String),
72 Decrement(String),
73 RemoveCounter(String),
74}
75
76#[relm4::component]
77impl SimpleComponent for App {
78 type Init = u8;
79 type Input = AppMsg;
80 type Output = ();
81
82 view! {
83 gtk::Window {
84 set_title: Some("Factory example"),
85 set_default_size: (300, 100),
86
87 gtk::Box {
88 set_orientation: gtk::Orientation::Vertical,
89 set_spacing: 5,
90 set_margin_all: 5,
91
92 gtk::StackSwitcher {
93 set_stack: Some(counter_stack),
94 },
95
96 gtk::Entry {
97 set_buffer: &model.entry_buffer,
98 connect_activate => AppMsg::AddCounter,
99 },
100
101 #[name(add_button)]
102 gtk::Button {
103 set_label: "Increment",
104 #[watch]
105 set_sensitive: counter_stack.visible_child().is_some(),
106 connect_clicked[sender, counter_stack] => move |_| {
107 if let Some(name) = counter_stack.visible_child_name() {
108 sender.input(AppMsg::Increment(name.into()));
109 }
110 },
111 },
112
113 #[name(remove_button)]
114 gtk::Button {
115 set_label: "Decrement",
116 #[watch]
117 set_sensitive: counter_stack.visible_child().is_some(),
118 connect_clicked[sender, counter_stack] => move |_| {
119 if let Some(name) = counter_stack.visible_child_name() {
120 sender.input(AppMsg::Decrement(name.into()));
121 }
122 },
123 },
124
125 gtk::Button {
126 set_label: "Remove counter",
127 #[watch]
128 set_sensitive: counter_stack.visible_child().is_some(),
129 connect_clicked[sender, counter_stack] => move |_| {
130 if let Some(name) = counter_stack.visible_child_name() {
131 sender.input(AppMsg::RemoveCounter(name.into()));
132 }
133 },
134 },
135
136 #[local_ref]
137 counter_stack -> gtk::Stack {
138 connect_visible_child_notify => AppMsg::UpdateView,
139 }
140 }
141 }
142 }
143
144 fn init(
145 counter: Self::Init,
146 root: Self::Root,
147 sender: ComponentSender<Self>,
148 ) -> ComponentParts<Self> {
149 let counters = FactoryHashMap::builder().launch_default().detach();
150
151 let model = App {
152 created_widgets: counter,
153 counters,
154 entry_buffer: gtk::EntryBuffer::default(),
155 };
156
157 let counter_stack = model.counters.widget();
158 let widgets = view_output!();
159
160 ComponentParts { model, widgets }
161 }
162
163 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
164 match msg {
165 AppMsg::AddCounter => {
166 let index = self.entry_buffer.text().to_string();
167 if !index.is_empty() {
168 self.counters.insert(index.clone(), self.created_widgets);
169 self.counters.set_visible(&index);
171 self.created_widgets = self.created_widgets.wrapping_add(1);
172 }
173 }
174 AppMsg::Increment(key) => {
175 let mut elem = self.counters.get_mut(&key).unwrap();
176 elem.value = elem.value.saturating_add(1);
177 }
178 AppMsg::Decrement(key) => {
179 let mut elem = self.counters.get_mut(&key).unwrap();
180 elem.value = elem.value.saturating_sub(1);
181 }
182 AppMsg::RemoveCounter(key) => {
183 self.counters.remove(&key);
184 }
185 AppMsg::UpdateView => (),
186 }
187 }
188}
189
190fn main() {
191 let app = RelmApp::new("relm4.example.factory");
192 app.run::<App>(0);
193}