FactoryVecDequeGuard

Struct FactoryVecDequeGuard 

Source
pub struct FactoryVecDequeGuard<'a, C>
where C: FactoryComponent<Index = DynamicIndex>,
{ /* private fields */ }
Expand description

Provides methods to edit the underlying FactoryVecDeque.

The changes will be rendered on the widgets after the guard goes out of scope.

Implementations§

Source§

impl<'a, C> FactoryVecDequeGuard<'a, C>
where C: FactoryComponent<Index = DynamicIndex>,

Source

pub fn drop(self)

Drops the guard and renders all changes.

Use this to transfer full ownership back to the FactoryVecDeque.

Examples found in repository?
relm4/examples/tab_game.rs (line 241)
213    fn init(
214        _: Self::Init,
215        root: Self::Root,
216        sender: ComponentSender<Self>,
217    ) -> ComponentParts<Self> {
218        let counters = FactoryVecDeque::builder()
219            .launch(adw::TabView::default())
220            .forward(sender.input_sender(), |output| match output {
221                CounterOutput::StartGame(index) => AppMsg::StartGame(index),
222                CounterOutput::SelectedGuess(guess) => AppMsg::SelectedGuess(guess),
223            });
224
225        let mut model = App {
226            counters,
227            start_index: None,
228        };
229
230        let tab_view = model.counters.widget();
231        let widgets = view_output!();
232
233        let mut counters_guard = model.counters.guard();
234        for i in 0..3 {
235            counters_guard.push_back(i);
236        }
237
238        // Explicitly drop the guard,
239        // so that 'model' is no longer borrowed
240        // and can be moved inside ComponentParts
241        counters_guard.drop();
242
243        ComponentParts { model, widgets }
244    }
Source

pub fn get_mut(&mut self, index: usize) -> Option<&mut C>

Tries to get a mutable reference to the model of one element.

Returns None if index is invalid.

Examples found in repository?
relm4/examples/entry.rs (line 123)
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    }
Source

pub fn back_mut(&mut self) -> Option<&mut C>

Provides a mutable reference to the model of the back element.

Returns None if the deque is empty.

Source

pub fn front_mut(&mut self) -> Option<&mut C>

Provides a mutable reference to the model of the front element.

Returns None if the deque is empty.

Source

pub fn pop_back(&mut self) -> Option<C>

Removes the last element from the FactoryVecDeque and returns it, or None if it is empty.

Examples found in repository?
relm4/examples/grid_factory.rs (line 218)
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
Hide additional examples
relm4/examples/tab_factory.rs (line 189)
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    }
relm4/examples/factory.rs (line 193)
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    }
Source

pub fn pop_front(&mut self) -> Option<C>

Removes the first element from the FactoryVecDeque and returns it, or None if it is empty.

Examples found in repository?
relm4/examples/entry.rs (line 114)
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    }
Source

pub fn remove(&mut self, index: usize) -> Option<C>

Removes and returns the element at index from the FactoryVecDeque. Returns None if index is out of bounds.

Element at index 0 is the front of the queue.

Examples found in repository?
relm4/examples/to_do.rs (line 132)
129    fn update(&mut self, msg: AppMsg, _sender: ComponentSender<Self>) {
130        match msg {
131            AppMsg::DeleteEntry(index) => {
132                self.tasks.guard().remove(index.current_index());
133            }
134            AppMsg::AddEntry(name) => {
135                self.tasks.guard().push_back(name);
136            }
137        }
138    }
More examples
Hide additional examples
relm4/examples/state_management.rs (line 142)
133    fn update(&mut self, message: Self::Input, _sender: FactorySender<Self>) {
134        match message {
135            TaskInput::ChangedName(name) => {
136                self.name = name;
137            }
138            TaskInput::AddedTag(name) => {
139                self.tags.guard().push_back(name);
140            }
141            TaskInput::DeletedTag(index) => {
142                self.tags.guard().remove(index);
143            }
144        }
145    }
146
147    fn init_widgets(
148        &mut self,
149        index: &Self::Index,
150        root: Self::Root,
151        _returned_widget: &<Self::ParentWidget as FactoryView>::ReturnedWidget,
152        sender: FactorySender<Self>,
153    ) -> Self::Widgets {
154        let tag_list_box = self.tags.widget();
155
156        let widgets = view_output!();
157
158        widgets
159    }
160
161    fn init_model(_name: Self::Init, index: &DynamicIndex, sender: FactorySender<Self>) -> Self {
162        let task_index = index.clone();
163
164        let tags = FactoryVecDeque::builder().launch_default().forward(
165            sender.output_sender(),
166            move |output| match output {
167                TagOutput::Delete(tag_index) => {
168                    TaskOutput::DeleteTag(task_index.clone(), tag_index)
169                }
170            },
171        );
172
173        Self {
174            name: "".into(),
175            tags,
176        }
177    }
178}
179
180#[derive(Debug)]
181struct Tag {
182    name: String,
183}
184
185#[derive(Debug)]
186enum TagInput {}
187
188#[derive(Debug)]
189enum TagOutput {
190    Delete(DynamicIndex),
191}
192
193#[relm4::factory]
194impl FactoryComponent for Tag {
195    type Init = String;
196    type Input = TagInput;
197    type Output = TagOutput;
198    type CommandOutput = ();
199    type ParentWidget = gtk::Box;
200
201    view! {
202        gtk::MenuButton {
203            #[watch]
204            set_label: &self.name,
205
206            #[wrap(Some)]
207            set_popover = &gtk::Popover {
208                gtk::Button {
209                    set_label: "Delete",
210
211                    connect_clicked[sender, index] => move |_| {
212                        sender.output(TagOutput::Delete(index.clone())).unwrap();
213                    }
214                }
215            }
216        }
217    }
218
219    fn init_model(name: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
220        Self { name }
221    }
222}
223
224/// The document is a headless component which holds and manages the data model.
225/// It receives input events FROM the App to update the data model.
226/// When updates to the model occur, it sends output events TO the App.
227///
228/// The document's interface is just input and output events. As a result you have a lot of freedom
229/// in how you choose to store the data model within the component, which backing store you use
230/// (such as the file system, a database, or a Web API), and how you synchronise to the backing
231/// store (e.g. manual save/load control, auto-saving on each change, batching up changes before
232/// syncing, and so on).
233struct Document {
234    /// The application data model.
235    /// In this case we have just stored the whole thing in memory because our requirements are
236    /// simple. In a real app you might choose a more elaborate approach.
237    model: Model,
238}
239
240#[derive(Default, Serialize, Deserialize)]
241struct TagModel {
242    name: String,
243}
244#[derive(Default, Serialize, Deserialize)]
245struct TaskModel {
246    name: String,
247    tags: Vec<TagModel>,
248}
249#[derive(Default, Serialize, Deserialize)]
250struct Model {
251    tasks: Vec<TaskModel>,
252}
253
254#[derive(Debug)]
255enum DocumentInput {
256    // extra operations on the document itself (in this case, related to file I/O)
257    Open(PathBuf),
258    Save(PathBuf),
259
260    // events related to the model that the document stores
261    Clear,
262    AddTask,
263    DeleteTask(DynamicIndex),
264    ChangeTaskName(DynamicIndex, String),
265    AddTag(DynamicIndex, String),
266    DeleteTag(DynamicIndex, DynamicIndex),
267}
268
269#[derive(Debug)]
270enum DocumentOutput {
271    Cleared,
272    AddedTask,
273    DeletedTask(usize),
274    ChangedTaskName(usize, String),
275    AddedTag(usize, String),
276    DeletedTag(usize, usize),
277}
278
279impl Worker for Document {
280    type Init = ();
281    type Input = DocumentInput;
282    type Output = DocumentOutput;
283
284    fn init(_init: Self::Init, _sender: ComponentSender<Self>) -> Self {
285        let model = Model::default();
286        Self { model }
287    }
288
289    fn update(&mut self, input: DocumentInput, sender: ComponentSender<Self>) {
290        match input {
291            DocumentInput::Save(path) => {
292                println!("Save as JSON to {path:?}");
293
294                // TODO in a real app you would report any errors from saving the document
295                if let Ok(json) = serde_json::to_string(&self.model) {
296                    std::fs::write(path, json).unwrap();
297                }
298            }
299            DocumentInput::Open(path) => {
300                println!("Open tasks document at {path:?}");
301
302                if let Ok(json) = std::fs::read_to_string(path)
303                    && let Ok(new_model) = serde_json::from_str(&json)
304                {
305                    // update the data model
306                    self.model = new_model;
307
308                    // refresh the view from the data model
309                    let _ = sender.output(DocumentOutput::Cleared);
310
311                    for (task_index, task) in self.model.tasks.iter().enumerate() {
312                        let _ = sender.output(DocumentOutput::AddedTask);
313
314                        let task_name = task.name.clone();
315                        let _ =
316                            sender.output(DocumentOutput::ChangedTaskName(task_index, task_name));
317
318                        for tag in &task.tags {
319                            let tag_name = tag.name.clone();
320                            let _ = sender.output(DocumentOutput::AddedTag(task_index, tag_name));
321                        }
322                    }
323                }
324            }
325            DocumentInput::Clear => {
326                self.model.tasks.clear();
327
328                let _ = sender.output(DocumentOutput::Cleared);
329            }
330            DocumentInput::AddTask => {
331                self.model.tasks.push(TaskModel::default());
332
333                let _ = sender.output(DocumentOutput::AddedTask);
334            }
335            DocumentInput::DeleteTask(index) => {
336                self.model.tasks.remove(index.current_index());
337
338                let _ = sender.output(DocumentOutput::DeletedTask(index.current_index()));
339            }
340            DocumentInput::ChangeTaskName(index, name) => {
341                if let Some(task) = self.model.tasks.get_mut(index.current_index()) {
342                    task.name.clone_from(&name);
343                }
344
345                // We don't technically need to send an event, because gtk::Entry updates itself
346                // this is just to make the example consistent.
347                let _ = sender.output(DocumentOutput::ChangedTaskName(index.current_index(), name));
348            }
349            DocumentInput::AddTag(task_index, name) => {
350                if let Some(task) = self.model.tasks.get_mut(task_index.current_index()) {
351                    task.tags.push(TagModel { name: name.clone() })
352                }
353
354                let _ = sender.output(DocumentOutput::AddedTag(task_index.current_index(), name));
355            }
356            DocumentInput::DeleteTag(task_index, tag_index) => {
357                if let Some(task) = self.model.tasks.get_mut(task_index.current_index()) {
358                    task.tags.remove(tag_index.current_index());
359                }
360
361                let _ = sender.output(DocumentOutput::DeletedTag(
362                    task_index.current_index(),
363                    tag_index.current_index(),
364                ));
365            }
366        }
367    }
368}
369
370/// The App is at the top level.
371/// It acts as a bridge between the view and the document, forwarding events between them.
372struct App {
373    view: FactoryVecDeque<Task>,
374    document: Controller<Document>,
375    save_dialog: Controller<SaveDialog>,
376    open_dialog: Controller<OpenDialog>,
377}
378
379#[derive(Debug)]
380enum AppInput {
381    Clear,
382    Cleared,
383
384    AddTask,
385    AddedTask,
386
387    DeleteTask(DynamicIndex),
388    DeletedTask(usize),
389
390    ChangeTaskName(DynamicIndex, String),
391    ChangedTaskName(usize, String),
392
393    AddTag(DynamicIndex, String),
394    AddedTag(usize, String),
395
396    DeleteTag(DynamicIndex, DynamicIndex),
397    DeletedTag(usize, usize),
398
399    // No-op event for when load/save dialogs result in Cancel
400    None,
401    Open,
402    OpenResponse(PathBuf),
403    Save,
404    SaveResponse(PathBuf),
405}
406
407#[relm4::component]
408impl SimpleComponent for App {
409    type Init = ();
410    type Input = AppInput;
411    type Output = ();
412
413    view! {
414        main_window = gtk::ApplicationWindow {
415            set_width_request: 360,
416            set_title: Some("Tasks"),
417
418            gtk::Box {
419                set_orientation: gtk::Orientation::Vertical,
420
421                gtk::HeaderBar {
422                    set_show_title_buttons: false,
423
424                    #[wrap(Some)]
425                    set_title_widget = &gtk::Label {
426                        set_text: ""
427                    },
428
429                    pack_start = &gtk::Button {
430                        set_icon_name: "plus",
431                        set_tooltip: "Add Task",
432
433                        connect_clicked[sender] => move |_| {
434                            sender.input(AppInput::AddTask);
435                        }
436                    },
437
438                    pack_end = &gtk::Button {
439                        set_label: "Save",
440                        connect_clicked => AppInput::Save,
441                    },
442                    pack_end = &gtk::Button {
443                        set_label: "Open",
444                        connect_clicked => AppInput::Open,
445                    },
446                },
447
448                gtk::ScrolledWindow {
449                    set_hscrollbar_policy: gtk::PolicyType::Never,
450                    set_min_content_height: 360,
451                    set_vexpand: true,
452
453                    #[local_ref]
454                    task_list_box -> gtk::ListBox {
455                        set_selection_mode: gtk::SelectionMode::None,
456                    }
457                },
458
459                gtk::Box {
460                    set_hexpand: true,
461                    set_spacing: DEFAULT_SPACING,
462                    set_orientation: gtk::Orientation::Horizontal,
463
464                    gtk::Label {
465                        set_text: "Press Enter after editing task names",
466                        set_hexpand: true,
467                        set_xalign: XALIGN_CENTER,
468                    },
469
470                    gtk::Button {
471                        set_icon_name: "edit-delete",
472                        set_tooltip: "Delete All Tasks",
473                        add_css_class: CSS_CLASS_DESTRUCTIVE_ACTION,
474
475                        connect_clicked[sender] => move |_| {
476                            sender.input(AppInput::Clear);
477                        }
478                    }
479                }
480            }
481        }
482    }
483
484    fn update(&mut self, msg: AppInput, _sender: ComponentSender<Self>) {
485        match msg {
486            AppInput::Clear => {
487                self.document.emit(DocumentInput::Clear);
488            }
489            AppInput::Cleared => {
490                self.view.guard().clear();
491            }
492            AppInput::AddTask => {
493                self.document.emit(DocumentInput::AddTask);
494            }
495            AppInput::AddedTask => {
496                self.view.guard().push_back(());
497            }
498            AppInput::DeleteTask(index) => {
499                self.document.emit(DocumentInput::DeleteTask(index));
500            }
501            AppInput::DeletedTask(index) => {
502                self.view.guard().remove(index);
503            }
504            AppInput::ChangeTaskName(index, name) => {
505                self.document
506                    .emit(DocumentInput::ChangeTaskName(index, name));
507            }
508            AppInput::ChangedTaskName(index, name) => {
509                self.view.guard().send(index, TaskInput::ChangedName(name));
510            }
511            AppInput::AddTag(index, name) => {
512                self.document.emit(DocumentInput::AddTag(index, name));
513            }
514            AppInput::AddedTag(index, name) => {
515                self.view.guard().send(index, TaskInput::AddedTag(name));
516            }
517            AppInput::DeleteTag(task_index, tag_index) => {
518                self.document
519                    .emit(DocumentInput::DeleteTag(task_index, tag_index));
520            }
521            AppInput::DeletedTag(task_index, tag_index) => {
522                self.view
523                    .guard()
524                    .send(task_index, TaskInput::DeletedTag(tag_index));
525            }
526            AppInput::None => {}
527            AppInput::Save => {
528                let name = "tasks.json".into();
529                self.save_dialog.emit(SaveDialogMsg::SaveAs(name));
530            }
531            AppInput::SaveResponse(path) => {
532                self.document.emit(DocumentInput::Save(path));
533            }
534            AppInput::Open => {
535                self.open_dialog.emit(OpenDialogMsg::Open);
536            }
537            AppInput::OpenResponse(path) => {
538                self.document.emit(DocumentInput::Open(path));
539            }
540        }
541    }
relm4/examples/factory.rs (line 214)
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    }
Source

pub fn push_back(&mut self, init: C::Init) -> DynamicIndex

Appends an element at the end of the FactoryVecDeque.

Examples found in repository?
relm4/examples/to_do.rs (line 135)
129    fn update(&mut self, msg: AppMsg, _sender: ComponentSender<Self>) {
130        match msg {
131            AppMsg::DeleteEntry(index) => {
132                self.tasks.guard().remove(index.current_index());
133            }
134            AppMsg::AddEntry(name) => {
135                self.tasks.guard().push_back(name);
136            }
137        }
138    }
More examples
Hide additional examples
relm4/examples/state_management.rs (line 139)
133    fn update(&mut self, message: Self::Input, _sender: FactorySender<Self>) {
134        match message {
135            TaskInput::ChangedName(name) => {
136                self.name = name;
137            }
138            TaskInput::AddedTag(name) => {
139                self.tags.guard().push_back(name);
140            }
141            TaskInput::DeletedTag(index) => {
142                self.tags.guard().remove(index);
143            }
144        }
145    }
146
147    fn init_widgets(
148        &mut self,
149        index: &Self::Index,
150        root: Self::Root,
151        _returned_widget: &<Self::ParentWidget as FactoryView>::ReturnedWidget,
152        sender: FactorySender<Self>,
153    ) -> Self::Widgets {
154        let tag_list_box = self.tags.widget();
155
156        let widgets = view_output!();
157
158        widgets
159    }
160
161    fn init_model(_name: Self::Init, index: &DynamicIndex, sender: FactorySender<Self>) -> Self {
162        let task_index = index.clone();
163
164        let tags = FactoryVecDeque::builder().launch_default().forward(
165            sender.output_sender(),
166            move |output| match output {
167                TagOutput::Delete(tag_index) => {
168                    TaskOutput::DeleteTag(task_index.clone(), tag_index)
169                }
170            },
171        );
172
173        Self {
174            name: "".into(),
175            tags,
176        }
177    }
178}
179
180#[derive(Debug)]
181struct Tag {
182    name: String,
183}
184
185#[derive(Debug)]
186enum TagInput {}
187
188#[derive(Debug)]
189enum TagOutput {
190    Delete(DynamicIndex),
191}
192
193#[relm4::factory]
194impl FactoryComponent for Tag {
195    type Init = String;
196    type Input = TagInput;
197    type Output = TagOutput;
198    type CommandOutput = ();
199    type ParentWidget = gtk::Box;
200
201    view! {
202        gtk::MenuButton {
203            #[watch]
204            set_label: &self.name,
205
206            #[wrap(Some)]
207            set_popover = &gtk::Popover {
208                gtk::Button {
209                    set_label: "Delete",
210
211                    connect_clicked[sender, index] => move |_| {
212                        sender.output(TagOutput::Delete(index.clone())).unwrap();
213                    }
214                }
215            }
216        }
217    }
218
219    fn init_model(name: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
220        Self { name }
221    }
222}
223
224/// The document is a headless component which holds and manages the data model.
225/// It receives input events FROM the App to update the data model.
226/// When updates to the model occur, it sends output events TO the App.
227///
228/// The document's interface is just input and output events. As a result you have a lot of freedom
229/// in how you choose to store the data model within the component, which backing store you use
230/// (such as the file system, a database, or a Web API), and how you synchronise to the backing
231/// store (e.g. manual save/load control, auto-saving on each change, batching up changes before
232/// syncing, and so on).
233struct Document {
234    /// The application data model.
235    /// In this case we have just stored the whole thing in memory because our requirements are
236    /// simple. In a real app you might choose a more elaborate approach.
237    model: Model,
238}
239
240#[derive(Default, Serialize, Deserialize)]
241struct TagModel {
242    name: String,
243}
244#[derive(Default, Serialize, Deserialize)]
245struct TaskModel {
246    name: String,
247    tags: Vec<TagModel>,
248}
249#[derive(Default, Serialize, Deserialize)]
250struct Model {
251    tasks: Vec<TaskModel>,
252}
253
254#[derive(Debug)]
255enum DocumentInput {
256    // extra operations on the document itself (in this case, related to file I/O)
257    Open(PathBuf),
258    Save(PathBuf),
259
260    // events related to the model that the document stores
261    Clear,
262    AddTask,
263    DeleteTask(DynamicIndex),
264    ChangeTaskName(DynamicIndex, String),
265    AddTag(DynamicIndex, String),
266    DeleteTag(DynamicIndex, DynamicIndex),
267}
268
269#[derive(Debug)]
270enum DocumentOutput {
271    Cleared,
272    AddedTask,
273    DeletedTask(usize),
274    ChangedTaskName(usize, String),
275    AddedTag(usize, String),
276    DeletedTag(usize, usize),
277}
278
279impl Worker for Document {
280    type Init = ();
281    type Input = DocumentInput;
282    type Output = DocumentOutput;
283
284    fn init(_init: Self::Init, _sender: ComponentSender<Self>) -> Self {
285        let model = Model::default();
286        Self { model }
287    }
288
289    fn update(&mut self, input: DocumentInput, sender: ComponentSender<Self>) {
290        match input {
291            DocumentInput::Save(path) => {
292                println!("Save as JSON to {path:?}");
293
294                // TODO in a real app you would report any errors from saving the document
295                if let Ok(json) = serde_json::to_string(&self.model) {
296                    std::fs::write(path, json).unwrap();
297                }
298            }
299            DocumentInput::Open(path) => {
300                println!("Open tasks document at {path:?}");
301
302                if let Ok(json) = std::fs::read_to_string(path)
303                    && let Ok(new_model) = serde_json::from_str(&json)
304                {
305                    // update the data model
306                    self.model = new_model;
307
308                    // refresh the view from the data model
309                    let _ = sender.output(DocumentOutput::Cleared);
310
311                    for (task_index, task) in self.model.tasks.iter().enumerate() {
312                        let _ = sender.output(DocumentOutput::AddedTask);
313
314                        let task_name = task.name.clone();
315                        let _ =
316                            sender.output(DocumentOutput::ChangedTaskName(task_index, task_name));
317
318                        for tag in &task.tags {
319                            let tag_name = tag.name.clone();
320                            let _ = sender.output(DocumentOutput::AddedTag(task_index, tag_name));
321                        }
322                    }
323                }
324            }
325            DocumentInput::Clear => {
326                self.model.tasks.clear();
327
328                let _ = sender.output(DocumentOutput::Cleared);
329            }
330            DocumentInput::AddTask => {
331                self.model.tasks.push(TaskModel::default());
332
333                let _ = sender.output(DocumentOutput::AddedTask);
334            }
335            DocumentInput::DeleteTask(index) => {
336                self.model.tasks.remove(index.current_index());
337
338                let _ = sender.output(DocumentOutput::DeletedTask(index.current_index()));
339            }
340            DocumentInput::ChangeTaskName(index, name) => {
341                if let Some(task) = self.model.tasks.get_mut(index.current_index()) {
342                    task.name.clone_from(&name);
343                }
344
345                // We don't technically need to send an event, because gtk::Entry updates itself
346                // this is just to make the example consistent.
347                let _ = sender.output(DocumentOutput::ChangedTaskName(index.current_index(), name));
348            }
349            DocumentInput::AddTag(task_index, name) => {
350                if let Some(task) = self.model.tasks.get_mut(task_index.current_index()) {
351                    task.tags.push(TagModel { name: name.clone() })
352                }
353
354                let _ = sender.output(DocumentOutput::AddedTag(task_index.current_index(), name));
355            }
356            DocumentInput::DeleteTag(task_index, tag_index) => {
357                if let Some(task) = self.model.tasks.get_mut(task_index.current_index()) {
358                    task.tags.remove(tag_index.current_index());
359                }
360
361                let _ = sender.output(DocumentOutput::DeletedTag(
362                    task_index.current_index(),
363                    tag_index.current_index(),
364                ));
365            }
366        }
367    }
368}
369
370/// The App is at the top level.
371/// It acts as a bridge between the view and the document, forwarding events between them.
372struct App {
373    view: FactoryVecDeque<Task>,
374    document: Controller<Document>,
375    save_dialog: Controller<SaveDialog>,
376    open_dialog: Controller<OpenDialog>,
377}
378
379#[derive(Debug)]
380enum AppInput {
381    Clear,
382    Cleared,
383
384    AddTask,
385    AddedTask,
386
387    DeleteTask(DynamicIndex),
388    DeletedTask(usize),
389
390    ChangeTaskName(DynamicIndex, String),
391    ChangedTaskName(usize, String),
392
393    AddTag(DynamicIndex, String),
394    AddedTag(usize, String),
395
396    DeleteTag(DynamicIndex, DynamicIndex),
397    DeletedTag(usize, usize),
398
399    // No-op event for when load/save dialogs result in Cancel
400    None,
401    Open,
402    OpenResponse(PathBuf),
403    Save,
404    SaveResponse(PathBuf),
405}
406
407#[relm4::component]
408impl SimpleComponent for App {
409    type Init = ();
410    type Input = AppInput;
411    type Output = ();
412
413    view! {
414        main_window = gtk::ApplicationWindow {
415            set_width_request: 360,
416            set_title: Some("Tasks"),
417
418            gtk::Box {
419                set_orientation: gtk::Orientation::Vertical,
420
421                gtk::HeaderBar {
422                    set_show_title_buttons: false,
423
424                    #[wrap(Some)]
425                    set_title_widget = &gtk::Label {
426                        set_text: ""
427                    },
428
429                    pack_start = &gtk::Button {
430                        set_icon_name: "plus",
431                        set_tooltip: "Add Task",
432
433                        connect_clicked[sender] => move |_| {
434                            sender.input(AppInput::AddTask);
435                        }
436                    },
437
438                    pack_end = &gtk::Button {
439                        set_label: "Save",
440                        connect_clicked => AppInput::Save,
441                    },
442                    pack_end = &gtk::Button {
443                        set_label: "Open",
444                        connect_clicked => AppInput::Open,
445                    },
446                },
447
448                gtk::ScrolledWindow {
449                    set_hscrollbar_policy: gtk::PolicyType::Never,
450                    set_min_content_height: 360,
451                    set_vexpand: true,
452
453                    #[local_ref]
454                    task_list_box -> gtk::ListBox {
455                        set_selection_mode: gtk::SelectionMode::None,
456                    }
457                },
458
459                gtk::Box {
460                    set_hexpand: true,
461                    set_spacing: DEFAULT_SPACING,
462                    set_orientation: gtk::Orientation::Horizontal,
463
464                    gtk::Label {
465                        set_text: "Press Enter after editing task names",
466                        set_hexpand: true,
467                        set_xalign: XALIGN_CENTER,
468                    },
469
470                    gtk::Button {
471                        set_icon_name: "edit-delete",
472                        set_tooltip: "Delete All Tasks",
473                        add_css_class: CSS_CLASS_DESTRUCTIVE_ACTION,
474
475                        connect_clicked[sender] => move |_| {
476                            sender.input(AppInput::Clear);
477                        }
478                    }
479                }
480            }
481        }
482    }
483
484    fn update(&mut self, msg: AppInput, _sender: ComponentSender<Self>) {
485        match msg {
486            AppInput::Clear => {
487                self.document.emit(DocumentInput::Clear);
488            }
489            AppInput::Cleared => {
490                self.view.guard().clear();
491            }
492            AppInput::AddTask => {
493                self.document.emit(DocumentInput::AddTask);
494            }
495            AppInput::AddedTask => {
496                self.view.guard().push_back(());
497            }
498            AppInput::DeleteTask(index) => {
499                self.document.emit(DocumentInput::DeleteTask(index));
500            }
501            AppInput::DeletedTask(index) => {
502                self.view.guard().remove(index);
503            }
504            AppInput::ChangeTaskName(index, name) => {
505                self.document
506                    .emit(DocumentInput::ChangeTaskName(index, name));
507            }
508            AppInput::ChangedTaskName(index, name) => {
509                self.view.guard().send(index, TaskInput::ChangedName(name));
510            }
511            AppInput::AddTag(index, name) => {
512                self.document.emit(DocumentInput::AddTag(index, name));
513            }
514            AppInput::AddedTag(index, name) => {
515                self.view.guard().send(index, TaskInput::AddedTag(name));
516            }
517            AppInput::DeleteTag(task_index, tag_index) => {
518                self.document
519                    .emit(DocumentInput::DeleteTag(task_index, tag_index));
520            }
521            AppInput::DeletedTag(task_index, tag_index) => {
522                self.view
523                    .guard()
524                    .send(task_index, TaskInput::DeletedTag(tag_index));
525            }
526            AppInput::None => {}
527            AppInput::Save => {
528                let name = "tasks.json".into();
529                self.save_dialog.emit(SaveDialogMsg::SaveAs(name));
530            }
531            AppInput::SaveResponse(path) => {
532                self.document.emit(DocumentInput::Save(path));
533            }
534            AppInput::Open => {
535                self.open_dialog.emit(OpenDialogMsg::Open);
536            }
537            AppInput::OpenResponse(path) => {
538                self.document.emit(DocumentInput::Open(path));
539            }
540        }
541    }
relm4/examples/tab_game.rs (line 235)
213    fn init(
214        _: Self::Init,
215        root: Self::Root,
216        sender: ComponentSender<Self>,
217    ) -> ComponentParts<Self> {
218        let counters = FactoryVecDeque::builder()
219            .launch(adw::TabView::default())
220            .forward(sender.input_sender(), |output| match output {
221                CounterOutput::StartGame(index) => AppMsg::StartGame(index),
222                CounterOutput::SelectedGuess(guess) => AppMsg::SelectedGuess(guess),
223            });
224
225        let mut model = App {
226            counters,
227            start_index: None,
228        };
229
230        let tab_view = model.counters.widget();
231        let widgets = view_output!();
232
233        let mut counters_guard = model.counters.guard();
234        for i in 0..3 {
235            counters_guard.push_back(i);
236        }
237
238        // Explicitly drop the guard,
239        // so that 'model' is no longer borrowed
240        // and can be moved inside ComponentParts
241        counters_guard.drop();
242
243        ComponentParts { model, widgets }
244    }
relm4/examples/grid_factory.rs (line 214)
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    }
relm4/examples/tab_factory.rs (line 185)
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    }
relm4/examples/entry.rs (line 108)
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    }
Source

pub fn push_front(&mut self, init: C::Init) -> DynamicIndex

Prepends an element to the FactoryVecDeque.

Source

pub fn insert(&mut self, index: usize, init: C::Init) -> DynamicIndex

Inserts an element at index within the FactoryVecDeque, shifting all elements with indices greater than or equal to index towards the back.

Element at index 0 is the front of the queue.

§Panics

Panics if index is greater than FactoryVecDeque’s length.

Source

pub fn swap(&mut self, first: usize, second: usize)

Swaps elements at indices first and second.

first and second may be equal.

Element at index 0 is the front of the queue.

§Panics

Panics if either index is out of bounds.

Examples found in repository?
relm4/examples/tab_game.rs (line 285)
273    fn update_cmd(
274        &mut self,
275        msg: Self::CommandOutput,
276        sender: ComponentSender<Self>,
277        _root: &Self::Root,
278    ) {
279        if msg {
280            sender.input(AppMsg::StopGame);
281        } else {
282            let mut counters_guard = self.counters.guard();
283            match rand::random::<u8>() % 3 {
284                0 => {
285                    counters_guard.swap(1, 2);
286                }
287                1 => {
288                    counters_guard.swap(0, 1);
289                }
290                _ => {
291                    let widget = counters_guard.widget();
292                    if !widget.select_next_page() {
293                        widget.select_previous_page();
294                    }
295                }
296            }
297        }
298    }
Source

pub fn move_to(&mut self, current_position: usize, target: usize)

Moves an element at index current_position to target, shifting all elements between these positions.

current_position and target may be equal.

Element at index 0 is the front of the queue.

§Panics

Panics if either index is out of bounds.

Examples found in repository?
relm4/examples/grid_factory.rs (line 228)
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
Hide additional examples
relm4/examples/tab_factory.rs (line 199)
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    }
relm4/examples/factory.rs (line 203)
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    }
Source

pub fn move_front(&mut self, current_position: usize)

Moves an element at index current_position to the front, shifting all elements between these positions.

§Panics

Panics if index is out of bounds.

Examples found in repository?
relm4/examples/grid_factory.rs (line 221)
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
Hide additional examples
relm4/examples/tab_factory.rs (line 192)
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    }
relm4/examples/factory.rs (line 196)
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    }
Source

pub fn move_back(&mut self, current_position: usize)

Moves an element at index current_position to the back, shifting all elements between these positions.

§Panics

Panics if index is out of bounds.

Source

pub fn clear(&mut self)

Remove all components from the FactoryVecDeque.

Examples found in repository?
relm4/examples/state_management.rs (line 490)
484    fn update(&mut self, msg: AppInput, _sender: ComponentSender<Self>) {
485        match msg {
486            AppInput::Clear => {
487                self.document.emit(DocumentInput::Clear);
488            }
489            AppInput::Cleared => {
490                self.view.guard().clear();
491            }
492            AppInput::AddTask => {
493                self.document.emit(DocumentInput::AddTask);
494            }
495            AppInput::AddedTask => {
496                self.view.guard().push_back(());
497            }
498            AppInput::DeleteTask(index) => {
499                self.document.emit(DocumentInput::DeleteTask(index));
500            }
501            AppInput::DeletedTask(index) => {
502                self.view.guard().remove(index);
503            }
504            AppInput::ChangeTaskName(index, name) => {
505                self.document
506                    .emit(DocumentInput::ChangeTaskName(index, name));
507            }
508            AppInput::ChangedTaskName(index, name) => {
509                self.view.guard().send(index, TaskInput::ChangedName(name));
510            }
511            AppInput::AddTag(index, name) => {
512                self.document.emit(DocumentInput::AddTag(index, name));
513            }
514            AppInput::AddedTag(index, name) => {
515                self.view.guard().send(index, TaskInput::AddedTag(name));
516            }
517            AppInput::DeleteTag(task_index, tag_index) => {
518                self.document
519                    .emit(DocumentInput::DeleteTag(task_index, tag_index));
520            }
521            AppInput::DeletedTag(task_index, tag_index) => {
522                self.view
523                    .guard()
524                    .send(task_index, TaskInput::DeletedTag(tag_index));
525            }
526            AppInput::None => {}
527            AppInput::Save => {
528                let name = "tasks.json".into();
529                self.save_dialog.emit(SaveDialogMsg::SaveAs(name));
530            }
531            AppInput::SaveResponse(path) => {
532                self.document.emit(DocumentInput::Save(path));
533            }
534            AppInput::Open => {
535                self.open_dialog.emit(OpenDialogMsg::Open);
536            }
537            AppInput::OpenResponse(path) => {
538                self.document.emit(DocumentInput::Open(path));
539            }
540        }
541    }
Source

pub fn iter_mut( &mut self, ) -> impl DoubleEndedIterator<Item = &mut C> + ExactSizeIterator + FusedIterator

Returns an iterator over the components that returns mutable references.

Methods from Deref<Target = FactoryVecDeque<C>>§

Source

pub fn len(&self) -> usize

Returns the number of elements in the FactoryVecDeque.

Examples found in repository?
relm4/examples/grid_factory.rs (line 227)
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
Hide additional examples
relm4/examples/tab_factory.rs (line 198)
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    }
relm4/examples/factory.rs (line 202)
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    }
Source

pub fn is_empty(&self) -> bool

Returns true if the FactoryVecDeque is empty.

Source

pub fn send(&self, index: usize, msg: C::Input)

Send a message to one of the elements.

Examples found in repository?
relm4/examples/state_management.rs (line 509)
484    fn update(&mut self, msg: AppInput, _sender: ComponentSender<Self>) {
485        match msg {
486            AppInput::Clear => {
487                self.document.emit(DocumentInput::Clear);
488            }
489            AppInput::Cleared => {
490                self.view.guard().clear();
491            }
492            AppInput::AddTask => {
493                self.document.emit(DocumentInput::AddTask);
494            }
495            AppInput::AddedTask => {
496                self.view.guard().push_back(());
497            }
498            AppInput::DeleteTask(index) => {
499                self.document.emit(DocumentInput::DeleteTask(index));
500            }
501            AppInput::DeletedTask(index) => {
502                self.view.guard().remove(index);
503            }
504            AppInput::ChangeTaskName(index, name) => {
505                self.document
506                    .emit(DocumentInput::ChangeTaskName(index, name));
507            }
508            AppInput::ChangedTaskName(index, name) => {
509                self.view.guard().send(index, TaskInput::ChangedName(name));
510            }
511            AppInput::AddTag(index, name) => {
512                self.document.emit(DocumentInput::AddTag(index, name));
513            }
514            AppInput::AddedTag(index, name) => {
515                self.view.guard().send(index, TaskInput::AddedTag(name));
516            }
517            AppInput::DeleteTag(task_index, tag_index) => {
518                self.document
519                    .emit(DocumentInput::DeleteTag(task_index, tag_index));
520            }
521            AppInput::DeletedTag(task_index, tag_index) => {
522                self.view
523                    .guard()
524                    .send(task_index, TaskInput::DeletedTag(tag_index));
525            }
526            AppInput::None => {}
527            AppInput::Save => {
528                let name = "tasks.json".into();
529                self.save_dialog.emit(SaveDialogMsg::SaveAs(name));
530            }
531            AppInput::SaveResponse(path) => {
532                self.document.emit(DocumentInput::Save(path));
533            }
534            AppInput::Open => {
535                self.open_dialog.emit(OpenDialogMsg::Open);
536            }
537            AppInput::OpenResponse(path) => {
538                self.document.emit(DocumentInput::Open(path));
539            }
540        }
541    }
Source

pub fn broadcast(&self, msg: C::Input)
where C::Input: Clone,

Send clone of a message to all of the elements.

Source

pub fn get(&self, index: usize) -> Option<&C>

Tries to get an immutable reference to the model of one element.

Returns None if index is invalid.

Source

pub fn back(&self) -> Option<&C>

Provides a reference to the model of the back element.

Returns None if the deque is empty.

Source

pub fn front(&self) -> Option<&C>

Provides a reference to the model of the front element.

Returns None if the deque is empty.

Source

pub fn widget(&self) -> &C::ParentWidget

Returns the widget all components are attached to.

Examples found in repository?
relm4/examples/state_management.rs (line 154)
147    fn init_widgets(
148        &mut self,
149        index: &Self::Index,
150        root: Self::Root,
151        _returned_widget: &<Self::ParentWidget as FactoryView>::ReturnedWidget,
152        sender: FactorySender<Self>,
153    ) -> Self::Widgets {
154        let tag_list_box = self.tags.widget();
155
156        let widgets = view_output!();
157
158        widgets
159    }
160
161    fn init_model(_name: Self::Init, index: &DynamicIndex, sender: FactorySender<Self>) -> Self {
162        let task_index = index.clone();
163
164        let tags = FactoryVecDeque::builder().launch_default().forward(
165            sender.output_sender(),
166            move |output| match output {
167                TagOutput::Delete(tag_index) => {
168                    TaskOutput::DeleteTag(task_index.clone(), tag_index)
169                }
170            },
171        );
172
173        Self {
174            name: "".into(),
175            tags,
176        }
177    }
178}
179
180#[derive(Debug)]
181struct Tag {
182    name: String,
183}
184
185#[derive(Debug)]
186enum TagInput {}
187
188#[derive(Debug)]
189enum TagOutput {
190    Delete(DynamicIndex),
191}
192
193#[relm4::factory]
194impl FactoryComponent for Tag {
195    type Init = String;
196    type Input = TagInput;
197    type Output = TagOutput;
198    type CommandOutput = ();
199    type ParentWidget = gtk::Box;
200
201    view! {
202        gtk::MenuButton {
203            #[watch]
204            set_label: &self.name,
205
206            #[wrap(Some)]
207            set_popover = &gtk::Popover {
208                gtk::Button {
209                    set_label: "Delete",
210
211                    connect_clicked[sender, index] => move |_| {
212                        sender.output(TagOutput::Delete(index.clone())).unwrap();
213                    }
214                }
215            }
216        }
217    }
218
219    fn init_model(name: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
220        Self { name }
221    }
222}
223
224/// The document is a headless component which holds and manages the data model.
225/// It receives input events FROM the App to update the data model.
226/// When updates to the model occur, it sends output events TO the App.
227///
228/// The document's interface is just input and output events. As a result you have a lot of freedom
229/// in how you choose to store the data model within the component, which backing store you use
230/// (such as the file system, a database, or a Web API), and how you synchronise to the backing
231/// store (e.g. manual save/load control, auto-saving on each change, batching up changes before
232/// syncing, and so on).
233struct Document {
234    /// The application data model.
235    /// In this case we have just stored the whole thing in memory because our requirements are
236    /// simple. In a real app you might choose a more elaborate approach.
237    model: Model,
238}
239
240#[derive(Default, Serialize, Deserialize)]
241struct TagModel {
242    name: String,
243}
244#[derive(Default, Serialize, Deserialize)]
245struct TaskModel {
246    name: String,
247    tags: Vec<TagModel>,
248}
249#[derive(Default, Serialize, Deserialize)]
250struct Model {
251    tasks: Vec<TaskModel>,
252}
253
254#[derive(Debug)]
255enum DocumentInput {
256    // extra operations on the document itself (in this case, related to file I/O)
257    Open(PathBuf),
258    Save(PathBuf),
259
260    // events related to the model that the document stores
261    Clear,
262    AddTask,
263    DeleteTask(DynamicIndex),
264    ChangeTaskName(DynamicIndex, String),
265    AddTag(DynamicIndex, String),
266    DeleteTag(DynamicIndex, DynamicIndex),
267}
268
269#[derive(Debug)]
270enum DocumentOutput {
271    Cleared,
272    AddedTask,
273    DeletedTask(usize),
274    ChangedTaskName(usize, String),
275    AddedTag(usize, String),
276    DeletedTag(usize, usize),
277}
278
279impl Worker for Document {
280    type Init = ();
281    type Input = DocumentInput;
282    type Output = DocumentOutput;
283
284    fn init(_init: Self::Init, _sender: ComponentSender<Self>) -> Self {
285        let model = Model::default();
286        Self { model }
287    }
288
289    fn update(&mut self, input: DocumentInput, sender: ComponentSender<Self>) {
290        match input {
291            DocumentInput::Save(path) => {
292                println!("Save as JSON to {path:?}");
293
294                // TODO in a real app you would report any errors from saving the document
295                if let Ok(json) = serde_json::to_string(&self.model) {
296                    std::fs::write(path, json).unwrap();
297                }
298            }
299            DocumentInput::Open(path) => {
300                println!("Open tasks document at {path:?}");
301
302                if let Ok(json) = std::fs::read_to_string(path)
303                    && let Ok(new_model) = serde_json::from_str(&json)
304                {
305                    // update the data model
306                    self.model = new_model;
307
308                    // refresh the view from the data model
309                    let _ = sender.output(DocumentOutput::Cleared);
310
311                    for (task_index, task) in self.model.tasks.iter().enumerate() {
312                        let _ = sender.output(DocumentOutput::AddedTask);
313
314                        let task_name = task.name.clone();
315                        let _ =
316                            sender.output(DocumentOutput::ChangedTaskName(task_index, task_name));
317
318                        for tag in &task.tags {
319                            let tag_name = tag.name.clone();
320                            let _ = sender.output(DocumentOutput::AddedTag(task_index, tag_name));
321                        }
322                    }
323                }
324            }
325            DocumentInput::Clear => {
326                self.model.tasks.clear();
327
328                let _ = sender.output(DocumentOutput::Cleared);
329            }
330            DocumentInput::AddTask => {
331                self.model.tasks.push(TaskModel::default());
332
333                let _ = sender.output(DocumentOutput::AddedTask);
334            }
335            DocumentInput::DeleteTask(index) => {
336                self.model.tasks.remove(index.current_index());
337
338                let _ = sender.output(DocumentOutput::DeletedTask(index.current_index()));
339            }
340            DocumentInput::ChangeTaskName(index, name) => {
341                if let Some(task) = self.model.tasks.get_mut(index.current_index()) {
342                    task.name.clone_from(&name);
343                }
344
345                // We don't technically need to send an event, because gtk::Entry updates itself
346                // this is just to make the example consistent.
347                let _ = sender.output(DocumentOutput::ChangedTaskName(index.current_index(), name));
348            }
349            DocumentInput::AddTag(task_index, name) => {
350                if let Some(task) = self.model.tasks.get_mut(task_index.current_index()) {
351                    task.tags.push(TagModel { name: name.clone() })
352                }
353
354                let _ = sender.output(DocumentOutput::AddedTag(task_index.current_index(), name));
355            }
356            DocumentInput::DeleteTag(task_index, tag_index) => {
357                if let Some(task) = self.model.tasks.get_mut(task_index.current_index()) {
358                    task.tags.remove(tag_index.current_index());
359                }
360
361                let _ = sender.output(DocumentOutput::DeletedTag(
362                    task_index.current_index(),
363                    tag_index.current_index(),
364                ));
365            }
366        }
367    }
368}
369
370/// The App is at the top level.
371/// It acts as a bridge between the view and the document, forwarding events between them.
372struct App {
373    view: FactoryVecDeque<Task>,
374    document: Controller<Document>,
375    save_dialog: Controller<SaveDialog>,
376    open_dialog: Controller<OpenDialog>,
377}
378
379#[derive(Debug)]
380enum AppInput {
381    Clear,
382    Cleared,
383
384    AddTask,
385    AddedTask,
386
387    DeleteTask(DynamicIndex),
388    DeletedTask(usize),
389
390    ChangeTaskName(DynamicIndex, String),
391    ChangedTaskName(usize, String),
392
393    AddTag(DynamicIndex, String),
394    AddedTag(usize, String),
395
396    DeleteTag(DynamicIndex, DynamicIndex),
397    DeletedTag(usize, usize),
398
399    // No-op event for when load/save dialogs result in Cancel
400    None,
401    Open,
402    OpenResponse(PathBuf),
403    Save,
404    SaveResponse(PathBuf),
405}
406
407#[relm4::component]
408impl SimpleComponent for App {
409    type Init = ();
410    type Input = AppInput;
411    type Output = ();
412
413    view! {
414        main_window = gtk::ApplicationWindow {
415            set_width_request: 360,
416            set_title: Some("Tasks"),
417
418            gtk::Box {
419                set_orientation: gtk::Orientation::Vertical,
420
421                gtk::HeaderBar {
422                    set_show_title_buttons: false,
423
424                    #[wrap(Some)]
425                    set_title_widget = &gtk::Label {
426                        set_text: ""
427                    },
428
429                    pack_start = &gtk::Button {
430                        set_icon_name: "plus",
431                        set_tooltip: "Add Task",
432
433                        connect_clicked[sender] => move |_| {
434                            sender.input(AppInput::AddTask);
435                        }
436                    },
437
438                    pack_end = &gtk::Button {
439                        set_label: "Save",
440                        connect_clicked => AppInput::Save,
441                    },
442                    pack_end = &gtk::Button {
443                        set_label: "Open",
444                        connect_clicked => AppInput::Open,
445                    },
446                },
447
448                gtk::ScrolledWindow {
449                    set_hscrollbar_policy: gtk::PolicyType::Never,
450                    set_min_content_height: 360,
451                    set_vexpand: true,
452
453                    #[local_ref]
454                    task_list_box -> gtk::ListBox {
455                        set_selection_mode: gtk::SelectionMode::None,
456                    }
457                },
458
459                gtk::Box {
460                    set_hexpand: true,
461                    set_spacing: DEFAULT_SPACING,
462                    set_orientation: gtk::Orientation::Horizontal,
463
464                    gtk::Label {
465                        set_text: "Press Enter after editing task names",
466                        set_hexpand: true,
467                        set_xalign: XALIGN_CENTER,
468                    },
469
470                    gtk::Button {
471                        set_icon_name: "edit-delete",
472                        set_tooltip: "Delete All Tasks",
473                        add_css_class: CSS_CLASS_DESTRUCTIVE_ACTION,
474
475                        connect_clicked[sender] => move |_| {
476                            sender.input(AppInput::Clear);
477                        }
478                    }
479                }
480            }
481        }
482    }
483
484    fn update(&mut self, msg: AppInput, _sender: ComponentSender<Self>) {
485        match msg {
486            AppInput::Clear => {
487                self.document.emit(DocumentInput::Clear);
488            }
489            AppInput::Cleared => {
490                self.view.guard().clear();
491            }
492            AppInput::AddTask => {
493                self.document.emit(DocumentInput::AddTask);
494            }
495            AppInput::AddedTask => {
496                self.view.guard().push_back(());
497            }
498            AppInput::DeleteTask(index) => {
499                self.document.emit(DocumentInput::DeleteTask(index));
500            }
501            AppInput::DeletedTask(index) => {
502                self.view.guard().remove(index);
503            }
504            AppInput::ChangeTaskName(index, name) => {
505                self.document
506                    .emit(DocumentInput::ChangeTaskName(index, name));
507            }
508            AppInput::ChangedTaskName(index, name) => {
509                self.view.guard().send(index, TaskInput::ChangedName(name));
510            }
511            AppInput::AddTag(index, name) => {
512                self.document.emit(DocumentInput::AddTag(index, name));
513            }
514            AppInput::AddedTag(index, name) => {
515                self.view.guard().send(index, TaskInput::AddedTag(name));
516            }
517            AppInput::DeleteTag(task_index, tag_index) => {
518                self.document
519                    .emit(DocumentInput::DeleteTag(task_index, tag_index));
520            }
521            AppInput::DeletedTag(task_index, tag_index) => {
522                self.view
523                    .guard()
524                    .send(task_index, TaskInput::DeletedTag(tag_index));
525            }
526            AppInput::None => {}
527            AppInput::Save => {
528                let name = "tasks.json".into();
529                self.save_dialog.emit(SaveDialogMsg::SaveAs(name));
530            }
531            AppInput::SaveResponse(path) => {
532                self.document.emit(DocumentInput::Save(path));
533            }
534            AppInput::Open => {
535                self.open_dialog.emit(OpenDialogMsg::Open);
536            }
537            AppInput::OpenResponse(path) => {
538                self.document.emit(DocumentInput::Open(path));
539            }
540        }
541    }
542
543    fn init(
544        _: Self::Init,
545        root: Self::Root,
546        sender: ComponentSender<Self>,
547    ) -> ComponentParts<Self> {
548        let view =
549            FactoryVecDeque::builder()
550                .launch_default()
551                .forward(sender.input_sender(), |msg| match msg {
552                    TaskOutput::Delete(index) => AppInput::DeleteTask(index),
553                    TaskOutput::Name(index, name) => AppInput::ChangeTaskName(index, name),
554                    TaskOutput::AddTag(index, name) => AppInput::AddTag(index, name),
555                    TaskOutput::DeleteTag(task_index, tag_index) => {
556                        AppInput::DeleteTag(task_index, tag_index)
557                    }
558                });
559
560        let document =
561            Document::builder()
562                .launch(())
563                .forward(sender.input_sender(), |msg| match msg {
564                    DocumentOutput::Cleared => AppInput::Cleared,
565                    DocumentOutput::DeletedTask(index) => AppInput::DeletedTask(index),
566                    DocumentOutput::DeletedTag(task_index, tag_index) => {
567                        AppInput::DeletedTag(task_index, tag_index)
568                    }
569                    DocumentOutput::AddedTask => AppInput::AddedTask,
570                    DocumentOutput::AddedTag(index, name) => AppInput::AddedTag(index, name),
571                    DocumentOutput::ChangedTaskName(index, name) => {
572                        AppInput::ChangedTaskName(index, name)
573                    }
574                });
575
576        let save_dialog = SaveDialog::builder()
577            .transient_for_native(&root)
578            .launch(SaveDialogSettings {
579                create_folders: true,
580                accept_label: "Save".into(),
581                cancel_label: "Cancel".into(),
582                is_modal: true,
583                filters: tasks_filename_filters(),
584            })
585            .forward(sender.input_sender(), |response| match response {
586                SaveDialogResponse::Accept(path) => AppInput::SaveResponse(path),
587                SaveDialogResponse::Cancel => AppInput::None,
588            });
589
590        let open_dialog = OpenDialog::builder()
591            .transient_for_native(&root)
592            .launch(OpenDialogSettings {
593                create_folders: false,
594                folder_mode: false,
595                cancel_label: "Cancel".into(),
596                accept_label: "Open".into(),
597                is_modal: true,
598                filters: tasks_filename_filters(),
599            })
600            .forward(sender.input_sender(), |response| match response {
601                OpenDialogResponse::Accept(path) => AppInput::OpenResponse(path),
602                OpenDialogResponse::Cancel => AppInput::None,
603            });
604
605        let app = App {
606            view,
607            document,
608            open_dialog,
609            save_dialog,
610        };
611
612        let task_list_box = app.view.widget();
613        let widgets = view_output!();
614
615        ComponentParts {
616            model: app,
617            widgets,
618        }
619    }
More examples
Hide additional examples
relm4/examples/to_do.rs (line 154)
140    fn init(
141        _: Self::Init,
142        root: Self::Root,
143        sender: ComponentSender<Self>,
144    ) -> ComponentParts<Self> {
145        let tasks =
146            FactoryVecDeque::builder()
147                .launch_default()
148                .forward(sender.input_sender(), |output| match output {
149                    TaskOutput::Delete(index) => AppMsg::DeleteEntry(index),
150                });
151
152        let model = App { tasks };
153
154        let task_list_box = model.tasks.widget();
155        let widgets = view_output!();
156
157        ComponentParts { model, widgets }
158    }
relm4/examples/tab_factory.rs (line 174)
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    }
relm4/examples/grid_factory.rs (line 203)
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    }
relm4/examples/factory.rs (line 179)
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    }
relm4/examples/tab_game.rs (line 230)
213    fn init(
214        _: Self::Init,
215        root: Self::Root,
216        sender: ComponentSender<Self>,
217    ) -> ComponentParts<Self> {
218        let counters = FactoryVecDeque::builder()
219            .launch(adw::TabView::default())
220            .forward(sender.input_sender(), |output| match output {
221                CounterOutput::StartGame(index) => AppMsg::StartGame(index),
222                CounterOutput::SelectedGuess(guess) => AppMsg::SelectedGuess(guess),
223            });
224
225        let mut model = App {
226            counters,
227            start_index: None,
228        };
229
230        let tab_view = model.counters.widget();
231        let widgets = view_output!();
232
233        let mut counters_guard = model.counters.guard();
234        for i in 0..3 {
235            counters_guard.push_back(i);
236        }
237
238        // Explicitly drop the guard,
239        // so that 'model' is no longer borrowed
240        // and can be moved inside ComponentParts
241        counters_guard.drop();
242
243        ComponentParts { model, widgets }
244    }
245
246    fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>, _root: &Self::Root) {
247        match msg {
248            AppMsg::StartGame(index) => {
249                self.start_index = Some(index);
250                sender.command(|sender, _| async move {
251                    for i in (1..4).rev() {
252                        *GAME_STATE.write() = GameState::Countdown(i);
253                        relm4::tokio::time::sleep(Duration::from_millis(1000)).await;
254                    }
255                    *GAME_STATE.write() = GameState::Running;
256                    for _ in 0..20 {
257                        relm4::tokio::time::sleep(Duration::from_millis(500)).await;
258                        sender.send(false).unwrap();
259                    }
260                    relm4::tokio::time::sleep(Duration::from_millis(1000)).await;
261                    sender.send(true).unwrap();
262                });
263            }
264            AppMsg::StopGame => {
265                *GAME_STATE.write() = GameState::Guessing;
266            }
267            AppMsg::SelectedGuess(index) => {
268                *GAME_STATE.write() = GameState::End(index == self.start_index.take().unwrap());
269            }
270        }
271    }
272
273    fn update_cmd(
274        &mut self,
275        msg: Self::CommandOutput,
276        sender: ComponentSender<Self>,
277        _root: &Self::Root,
278    ) {
279        if msg {
280            sender.input(AppMsg::StopGame);
281        } else {
282            let mut counters_guard = self.counters.guard();
283            match rand::random::<u8>() % 3 {
284                0 => {
285                    counters_guard.swap(1, 2);
286                }
287                1 => {
288                    counters_guard.swap(0, 1);
289                }
290                _ => {
291                    let widget = counters_guard.widget();
292                    if !widget.select_next_page() {
293                        widget.select_previous_page();
294                    }
295                }
296            }
297        }
298    }
Source

pub fn iter( &self, ) -> impl DoubleEndedIterator<Item = &C> + ExactSizeIterator + FusedIterator

Returns an iterator over the components.

Source

pub fn set_visible(&self, index: usize) -> bool

Makes the element at a given index visible in a gtk::Stack. Returns true on success, otherwise false.

Trait Implementations§

Source§

impl<'a, C> Debug for FactoryVecDequeGuard<'a, C>
where C: FactoryComponent<Index = DynamicIndex> + Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<C> Deref for FactoryVecDequeGuard<'_, C>
where C: FactoryComponent<Index = DynamicIndex>,

Source§

type Target = FactoryVecDeque<C>

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Self::Target

Dereferences the value.
Source§

impl<C> Drop for FactoryVecDequeGuard<'_, C>
where C: FactoryComponent<Index = DynamicIndex>,

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl<C> Index<usize> for FactoryVecDequeGuard<'_, C>
where C: FactoryComponent<Index = DynamicIndex>,

Source§

type Output = C

The returned type after indexing.
Source§

fn index(&self, index: usize) -> &Self::Output

Performs the indexing (container[index]) operation. Read more
Source§

impl<C> IndexMut<usize> for FactoryVecDequeGuard<'_, C>
where C: FactoryComponent<Index = DynamicIndex>,

Source§

fn index_mut(&mut self, index: usize) -> &mut Self::Output

Performs the mutable indexing (container[index]) operation. Read more

Auto Trait Implementations§

§

impl<'a, C> Freeze for FactoryVecDequeGuard<'a, C>

§

impl<'a, C> !RefUnwindSafe for FactoryVecDequeGuard<'a, C>

§

impl<'a, C> !Send for FactoryVecDequeGuard<'a, C>

§

impl<'a, C> !Sync for FactoryVecDequeGuard<'a, C>

§

impl<'a, C> Unpin for FactoryVecDequeGuard<'a, C>

§

impl<'a, C> !UnwindSafe for FactoryVecDequeGuard<'a, C>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<C> AsyncPosition<()> for C

Source§

fn position(_index: usize)

Returns the position. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<C, I> Position<(), I> for C

Source§

fn position(&self, _index: &I)

Returns the position. Read more
Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more