pub struct DynamicIndex { /* private fields */ }Expand description
A dynamic index that updates automatically when items are shifted inside a factory container.
For example a FactoryVecDeque has an insert
method that allows users to insert data at arbitrary positions.
If we insert at the front all following widgets will be moved by one which would
invalidate their indices.
To allow widgets in a factory container to send messages with valid indices
this type ensures that the indices is always up to date.
Never send an index as usize but always as DynamicIndex
to the update function because messages can be queued up and stale by the time they are handled.
DynamicIndex is a smart pointer so cloning will work similar to std::rc::Rc and will create
a pointer to the same data.
In short: only call current_index from the update function
where you actually need the index as usize.
Implementations§
Source§impl DynamicIndex
impl DynamicIndex
Sourcepub fn current_index(&self) -> usize
pub fn current_index(&self) -> usize
Get the current index number.
This value is updated by the factory container and might change after each update function.
Examples found in repository?
29 fn position(&self, index: &DynamicIndex) -> GridPosition {
30 let index = index.current_index();
31 let x = index % 5;
32 let y = index / 5;
33 GridPosition {
34 column: y as i32,
35 row: x as i32,
36 width: 1,
37 height: 1,
38 }
39 }
40}
41
42impl FactoryComponent for Counter {
43 type Init = u8;
44 type Input = CounterMsg;
45 type Output = CounterOutput;
46 type CommandOutput = ();
47 type Root = gtk::Box;
48 type Widgets = CounterWidgets;
49 type ParentWidget = gtk::Grid;
50 type Index = DynamicIndex;
51
52 fn init_root(&self) -> Self::Root {
53 relm4::view! {
54 root = gtk::Box {
55 set_orientation: gtk::Orientation::Horizontal,
56 set_spacing: 10,
57 }
58 }
59 root
60 }
61
62 fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
63 Self { value }
64 }
65
66 fn init_widgets(
67 &mut self,
68 index: &DynamicIndex,
69 root: Self::Root,
70 _returned_widget: >k::Widget,
71 sender: FactorySender<Self>,
72 ) -> Self::Widgets {
73 relm4::view! {
74 #[local_ref]
75 root -> gtk::Box {
76 #[name(label)]
77 gtk::Label {
78 set_label: &self.value.to_string(),
79 set_width_chars: 3,
80 },
81
82 gtk::Button {
83 set_label: "+",
84 connect_clicked => CounterMsg::Increment,
85 },
86
87 gtk::Button {
88 set_label: "-",
89 connect_clicked => CounterMsg::Decrement,
90 },
91
92 gtk::Button {
93 set_label: "Up",
94 connect_clicked[sender, index] => move |_| {
95 sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
96 }
97 },
98
99 gtk::Button {
100 set_label: "Down",
101 connect_clicked[sender, index] => move |_| {
102 sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
103 }
104 },
105
106 gtk::Button {
107 set_label: "To Start",
108 connect_clicked[sender, index] => move |_| {
109 sender.output(CounterOutput::SendFront(index.clone())).unwrap();
110 }
111 }
112 }
113 }
114
115 CounterWidgets { label }
116 }
117
118 fn update(&mut self, msg: Self::Input, _sender: FactorySender<Self>) {
119 match msg {
120 CounterMsg::Increment => {
121 self.value = self.value.wrapping_add(1);
122 }
123 CounterMsg::Decrement => {
124 self.value = self.value.wrapping_sub(1);
125 }
126 }
127 }
128
129 fn update_view(&self, widgets: &mut Self::Widgets, _sender: FactorySender<Self>) {
130 widgets.label.set_label(&self.value.to_string());
131 }
132}
133
134struct App {
135 created_widgets: u8,
136 counters: FactoryVecDeque<Counter>,
137}
138
139#[derive(Debug)]
140enum AppMsg {
141 AddCounter,
142 RemoveCounter,
143 SendFront(DynamicIndex),
144 MoveUp(DynamicIndex),
145 MoveDown(DynamicIndex),
146}
147
148#[relm4::component]
149impl SimpleComponent for App {
150 type Init = u8;
151 type Input = AppMsg;
152 type Output = ();
153
154 view! {
155 gtk::Window {
156 set_title: Some("Grid factory example"),
157 set_default_size: (300, 100),
158
159 gtk::Box {
160 set_orientation: gtk::Orientation::Vertical,
161 set_spacing: 5,
162 set_margin_all: 5,
163
164 gtk::Button {
165 set_label: "Add counter",
166 connect_clicked => AppMsg::AddCounter,
167 },
168
169 gtk::Button {
170 set_label: "Remove counter",
171 connect_clicked => AppMsg::RemoveCounter,
172 },
173
174 #[local_ref]
175 counter_grid -> gtk::Grid {
176 set_orientation: gtk::Orientation::Vertical,
177 set_column_spacing: 15,
178 set_row_spacing: 5,
179 }
180 }
181 }
182 }
183
184 fn init(
185 counter: Self::Init,
186 root: Self::Root,
187 sender: ComponentSender<Self>,
188 ) -> ComponentParts<Self> {
189 let counters =
190 FactoryVecDeque::builder()
191 .launch_default()
192 .forward(sender.input_sender(), |msg| match msg {
193 CounterOutput::SendFront(index) => AppMsg::SendFront(index),
194 CounterOutput::MoveUp(index) => AppMsg::MoveUp(index),
195 CounterOutput::MoveDown(index) => AppMsg::MoveDown(index),
196 });
197
198 let model = App {
199 created_widgets: counter,
200 counters,
201 };
202
203 let counter_grid = model.counters.widget();
204 let widgets = view_output!();
205
206 ComponentParts { model, widgets }
207 }
208
209 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
210 let mut counters_guard = self.counters.guard();
211
212 match msg {
213 AppMsg::AddCounter => {
214 counters_guard.push_back(self.created_widgets);
215 self.created_widgets = self.created_widgets.wrapping_add(1);
216 }
217 AppMsg::RemoveCounter => {
218 counters_guard.pop_back();
219 }
220 AppMsg::SendFront(index) => {
221 counters_guard.move_front(index.current_index());
222 }
223 AppMsg::MoveDown(index) => {
224 let index = index.current_index();
225 let new_index = index + 1;
226 // Already at the end?
227 if new_index < counters_guard.len() {
228 counters_guard.move_to(index, new_index);
229 }
230 }
231 AppMsg::MoveUp(index) => {
232 let index = index.current_index();
233 // Already at the start?
234 if index != 0 {
235 counters_guard.move_to(index, index - 1);
236 }
237 }
238 }
239 }More examples
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 // Already at the end?
198 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 // Already at the start?
205 if index != 0 {
206 counters_guard.move_to(index, index - 1);
207 }
208 }
209 }
210 }99 fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
100 match message {
101 AppMsg::AddCounters => {
102 let text = self.entry.text();
103 if let Ok(v) = text.parse::<i32>() {
104 let mut guard = self.counters.guard();
105 if v.is_positive() {
106 // add as many counters as user entered
107 for _ in 0..v {
108 guard.push_back(self.created_counters);
109 self.created_counters += 1;
110 }
111 } else if v.is_negative() {
112 // remove counters
113 for _ in v..0 {
114 guard.pop_front();
115 }
116 }
117
118 // clearing the entry value clears the entry widget
119 self.entry.set_text("");
120 }
121 }
122 AppMsg::Clicked(index) => {
123 if let Some(counter) = self.counters.guard().get_mut(index.current_index()) {
124 counter.value = counter.value.wrapping_sub(1);
125 }
126 }
127 }
128 }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 // Already at the end?
202 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 // Already at the start?
209 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 }213 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
214 let mut counters_guard = self.counters.guard();
215 match msg {
216 AppMsg::AddCounter => {
217 counters_guard.push_back(self.created_widgets);
218 self.created_widgets = self.created_widgets.wrapping_add(1);
219 }
220 AppMsg::RemoveCounter => {
221 counters_guard.pop_back();
222 }
223 AppMsg::SendFront(index) => {
224 counters_guard.move_front(index.current_index());
225 }
226 AppMsg::MoveDown(index) => {
227 let index = index.current_index();
228 let new_index = index + 1;
229 // Already at the end?
230 if new_index < counters_guard.len() {
231 counters_guard.move_to(index, new_index);
232 }
233 }
234 AppMsg::MoveUp(index) => {
235 let index = index.current_index();
236 // Already at the start?
237 if index != 0 {
238 counters_guard.move_to(index, index - 1);
239 }
240 }
241 AppMsg::Remove(index) => {
242 counters_guard.remove(index.current_index());
243 }
244 }
245 }