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>,
impl<'a, C> FactoryVecDequeGuard<'a, C>where
C: FactoryComponent<Index = DynamicIndex>,
Sourcepub fn drop(self)
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?
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 }Sourcepub fn get_mut(&mut self, index: usize) -> Option<&mut C>
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?
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 }Sourcepub fn back_mut(&mut self) -> Option<&mut C>
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.
Sourcepub fn front_mut(&mut self) -> Option<&mut C>
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.
Sourcepub fn pop_back(&mut self) -> Option<C>
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?
209 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
210 let mut counters_guard = self.counters.guard();
211
212 match msg {
213 AppMsg::AddCounter => {
214 counters_guard.push_back(self.created_widgets);
215 self.created_widgets = self.created_widgets.wrapping_add(1);
216 }
217 AppMsg::RemoveCounter => {
218 counters_guard.pop_back();
219 }
220 AppMsg::SendFront(index) => {
221 counters_guard.move_front(index.current_index());
222 }
223 AppMsg::MoveDown(index) => {
224 let index = index.current_index();
225 let new_index = index + 1;
226 // Already at the end?
227 if new_index < counters_guard.len() {
228 counters_guard.move_to(index, new_index);
229 }
230 }
231 AppMsg::MoveUp(index) => {
232 let index = index.current_index();
233 // Already at the start?
234 if index != 0 {
235 counters_guard.move_to(index, index - 1);
236 }
237 }
238 }
239 }More examples
180 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
181 let mut counters_guard = self.counters.guard();
182
183 match msg {
184 AppMsg::AddCounter => {
185 counters_guard.push_back(self.created_widgets);
186 self.created_widgets = self.created_widgets.wrapping_add(1);
187 }
188 AppMsg::RemoveCounter => {
189 counters_guard.pop_back();
190 }
191 AppMsg::SendFront(index) => {
192 counters_guard.move_front(index.current_index());
193 }
194 AppMsg::MoveDown(index) => {
195 let index = index.current_index();
196 let new_index = index + 1;
197 // Already at the end?
198 if new_index < counters_guard.len() {
199 counters_guard.move_to(index, new_index);
200 }
201 }
202 AppMsg::MoveUp(index) => {
203 let index = index.current_index();
204 // Already at the start?
205 if index != 0 {
206 counters_guard.move_to(index, index - 1);
207 }
208 }
209 }
210 }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 }Sourcepub fn pop_front(&mut self) -> Option<C>
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?
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 }Sourcepub fn remove(&mut self, index: usize) -> Option<C>
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?
More examples
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 = >k::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 = >k::Label {
426 set_text: ""
427 },
428
429 pack_start = >k::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 = >k::Button {
439 set_label: "Save",
440 connect_clicked => AppInput::Save,
441 },
442 pack_end = >k::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 }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 }Sourcepub fn push_back(&mut self, init: C::Init) -> DynamicIndex
pub fn push_back(&mut self, init: C::Init) -> DynamicIndex
Appends an element at the end of the FactoryVecDeque.
Examples found in repository?
More examples
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 = >k::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 = >k::Label {
426 set_text: ""
427 },
428
429 pack_start = >k::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 = >k::Button {
439 set_label: "Save",
440 connect_clicked => AppInput::Save,
441 },
442 pack_end = >k::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 }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 }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 }180 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
181 let mut counters_guard = self.counters.guard();
182
183 match msg {
184 AppMsg::AddCounter => {
185 counters_guard.push_back(self.created_widgets);
186 self.created_widgets = self.created_widgets.wrapping_add(1);
187 }
188 AppMsg::RemoveCounter => {
189 counters_guard.pop_back();
190 }
191 AppMsg::SendFront(index) => {
192 counters_guard.move_front(index.current_index());
193 }
194 AppMsg::MoveDown(index) => {
195 let index = index.current_index();
196 let new_index = index + 1;
197 // Already at the end?
198 if new_index < counters_guard.len() {
199 counters_guard.move_to(index, new_index);
200 }
201 }
202 AppMsg::MoveUp(index) => {
203 let index = index.current_index();
204 // Already at the start?
205 if index != 0 {
206 counters_guard.move_to(index, index - 1);
207 }
208 }
209 }
210 }99 fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
100 match message {
101 AppMsg::AddCounters => {
102 let text = self.entry.text();
103 if let Ok(v) = text.parse::<i32>() {
104 let mut guard = self.counters.guard();
105 if v.is_positive() {
106 // add as many counters as user entered
107 for _ in 0..v {
108 guard.push_back(self.created_counters);
109 self.created_counters += 1;
110 }
111 } else if v.is_negative() {
112 // remove counters
113 for _ in v..0 {
114 guard.pop_front();
115 }
116 }
117
118 // clearing the entry value clears the entry widget
119 self.entry.set_text("");
120 }
121 }
122 AppMsg::Clicked(index) => {
123 if let Some(counter) = self.counters.guard().get_mut(index.current_index()) {
124 counter.value = counter.value.wrapping_sub(1);
125 }
126 }
127 }
128 }Sourcepub fn push_front(&mut self, init: C::Init) -> DynamicIndex
pub fn push_front(&mut self, init: C::Init) -> DynamicIndex
Prepends an element to the FactoryVecDeque.
Sourcepub fn insert(&mut self, index: usize, init: C::Init) -> DynamicIndex
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.
Sourcepub fn swap(&mut self, first: usize, second: usize)
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?
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 }Sourcepub fn move_to(&mut self, current_position: usize, target: usize)
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?
209 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
210 let mut counters_guard = self.counters.guard();
211
212 match msg {
213 AppMsg::AddCounter => {
214 counters_guard.push_back(self.created_widgets);
215 self.created_widgets = self.created_widgets.wrapping_add(1);
216 }
217 AppMsg::RemoveCounter => {
218 counters_guard.pop_back();
219 }
220 AppMsg::SendFront(index) => {
221 counters_guard.move_front(index.current_index());
222 }
223 AppMsg::MoveDown(index) => {
224 let index = index.current_index();
225 let new_index = index + 1;
226 // Already at the end?
227 if new_index < counters_guard.len() {
228 counters_guard.move_to(index, new_index);
229 }
230 }
231 AppMsg::MoveUp(index) => {
232 let index = index.current_index();
233 // Already at the start?
234 if index != 0 {
235 counters_guard.move_to(index, index - 1);
236 }
237 }
238 }
239 }More examples
180 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
181 let mut counters_guard = self.counters.guard();
182
183 match msg {
184 AppMsg::AddCounter => {
185 counters_guard.push_back(self.created_widgets);
186 self.created_widgets = self.created_widgets.wrapping_add(1);
187 }
188 AppMsg::RemoveCounter => {
189 counters_guard.pop_back();
190 }
191 AppMsg::SendFront(index) => {
192 counters_guard.move_front(index.current_index());
193 }
194 AppMsg::MoveDown(index) => {
195 let index = index.current_index();
196 let new_index = index + 1;
197 // Already at the end?
198 if new_index < counters_guard.len() {
199 counters_guard.move_to(index, new_index);
200 }
201 }
202 AppMsg::MoveUp(index) => {
203 let index = index.current_index();
204 // Already at the start?
205 if index != 0 {
206 counters_guard.move_to(index, index - 1);
207 }
208 }
209 }
210 }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 }Sourcepub fn move_front(&mut self, current_position: usize)
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?
209 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
210 let mut counters_guard = self.counters.guard();
211
212 match msg {
213 AppMsg::AddCounter => {
214 counters_guard.push_back(self.created_widgets);
215 self.created_widgets = self.created_widgets.wrapping_add(1);
216 }
217 AppMsg::RemoveCounter => {
218 counters_guard.pop_back();
219 }
220 AppMsg::SendFront(index) => {
221 counters_guard.move_front(index.current_index());
222 }
223 AppMsg::MoveDown(index) => {
224 let index = index.current_index();
225 let new_index = index + 1;
226 // Already at the end?
227 if new_index < counters_guard.len() {
228 counters_guard.move_to(index, new_index);
229 }
230 }
231 AppMsg::MoveUp(index) => {
232 let index = index.current_index();
233 // Already at the start?
234 if index != 0 {
235 counters_guard.move_to(index, index - 1);
236 }
237 }
238 }
239 }More examples
180 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
181 let mut counters_guard = self.counters.guard();
182
183 match msg {
184 AppMsg::AddCounter => {
185 counters_guard.push_back(self.created_widgets);
186 self.created_widgets = self.created_widgets.wrapping_add(1);
187 }
188 AppMsg::RemoveCounter => {
189 counters_guard.pop_back();
190 }
191 AppMsg::SendFront(index) => {
192 counters_guard.move_front(index.current_index());
193 }
194 AppMsg::MoveDown(index) => {
195 let index = index.current_index();
196 let new_index = index + 1;
197 // Already at the end?
198 if new_index < counters_guard.len() {
199 counters_guard.move_to(index, new_index);
200 }
201 }
202 AppMsg::MoveUp(index) => {
203 let index = index.current_index();
204 // Already at the start?
205 if index != 0 {
206 counters_guard.move_to(index, index - 1);
207 }
208 }
209 }
210 }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 }Sourcepub fn move_back(&mut self, current_position: usize)
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.
Sourcepub fn clear(&mut self)
pub fn clear(&mut self)
Remove all components from the FactoryVecDeque.
Examples found in repository?
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 }Sourcepub fn iter_mut(
&mut self,
) -> impl DoubleEndedIterator<Item = &mut C> + ExactSizeIterator + FusedIterator
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>>§
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of elements in the FactoryVecDeque.
Examples found in repository?
209 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
210 let mut counters_guard = self.counters.guard();
211
212 match msg {
213 AppMsg::AddCounter => {
214 counters_guard.push_back(self.created_widgets);
215 self.created_widgets = self.created_widgets.wrapping_add(1);
216 }
217 AppMsg::RemoveCounter => {
218 counters_guard.pop_back();
219 }
220 AppMsg::SendFront(index) => {
221 counters_guard.move_front(index.current_index());
222 }
223 AppMsg::MoveDown(index) => {
224 let index = index.current_index();
225 let new_index = index + 1;
226 // Already at the end?
227 if new_index < counters_guard.len() {
228 counters_guard.move_to(index, new_index);
229 }
230 }
231 AppMsg::MoveUp(index) => {
232 let index = index.current_index();
233 // Already at the start?
234 if index != 0 {
235 counters_guard.move_to(index, index - 1);
236 }
237 }
238 }
239 }More examples
180 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
181 let mut counters_guard = self.counters.guard();
182
183 match msg {
184 AppMsg::AddCounter => {
185 counters_guard.push_back(self.created_widgets);
186 self.created_widgets = self.created_widgets.wrapping_add(1);
187 }
188 AppMsg::RemoveCounter => {
189 counters_guard.pop_back();
190 }
191 AppMsg::SendFront(index) => {
192 counters_guard.move_front(index.current_index());
193 }
194 AppMsg::MoveDown(index) => {
195 let index = index.current_index();
196 let new_index = index + 1;
197 // Already at the end?
198 if new_index < counters_guard.len() {
199 counters_guard.move_to(index, new_index);
200 }
201 }
202 AppMsg::MoveUp(index) => {
203 let index = index.current_index();
204 // Already at the start?
205 if index != 0 {
206 counters_guard.move_to(index, index - 1);
207 }
208 }
209 }
210 }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 }Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true if the FactoryVecDeque is empty.
Sourcepub fn send(&self, index: usize, msg: C::Input)
pub fn send(&self, index: usize, msg: C::Input)
Send a message to one of the elements.
Examples found in repository?
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 }Sourcepub fn get(&self, index: usize) -> Option<&C>
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.
Sourcepub fn back(&self) -> Option<&C>
pub fn back(&self) -> Option<&C>
Provides a reference to the model of the back element.
Returns None if the deque is empty.
Sourcepub fn front(&self) -> Option<&C>
pub fn front(&self) -> Option<&C>
Provides a reference to the model of the front element.
Returns None if the deque is empty.
Sourcepub fn widget(&self) -> &C::ParentWidget
pub fn widget(&self) -> &C::ParentWidget
Returns the widget all components are attached to.
Examples found in repository?
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 = >k::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 = >k::Label {
426 set_text: ""
427 },
428
429 pack_start = >k::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 = >k::Button {
439 set_label: "Save",
440 connect_clicked => AppInput::Save,
441 },
442 pack_end = >k::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
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 }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 }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 }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 }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 }Sourcepub fn iter(
&self,
) -> impl DoubleEndedIterator<Item = &C> + ExactSizeIterator + FusedIterator
pub fn iter( &self, ) -> impl DoubleEndedIterator<Item = &C> + ExactSizeIterator + FusedIterator
Returns an iterator over the components.
Sourcepub fn set_visible(&self, index: usize) -> bool
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.