DynamicIndex

Struct DynamicIndex 

Source
pub struct DynamicIndex { /* private fields */ }
Expand description

A dynamic index that updates automatically when items are shifted inside a factory container.

For example a FactoryVecDeque has an insert method that allows users to insert data at arbitrary positions. If we insert at the front all following widgets will be moved by one which would invalidate their indices. To allow widgets in a factory container to send messages with valid indices this type ensures that the indices is always up to date.

Never send an index as usize but always as DynamicIndex to the update function because messages can be queued up and stale by the time they are handled.

DynamicIndex is a smart pointer so cloning will work similar to std::rc::Rc and will create a pointer to the same data.

In short: only call current_index from the update function where you actually need the index as usize.

Implementations§

Source§

impl DynamicIndex

Source

pub fn current_index(&self) -> usize

Get the current index number.

This value is updated by the factory container and might change after each update function.

Examples found in repository?
relm4/examples/grid_factory.rs (line 30)
29    fn position(&self, index: &DynamicIndex) -> GridPosition {
30        let index = index.current_index();
31        let x = index % 5;
32        let y = index / 5;
33        GridPosition {
34            column: y as i32,
35            row: x as i32,
36            width: 1,
37            height: 1,
38        }
39    }
40}
41
42impl FactoryComponent for Counter {
43    type Init = u8;
44    type Input = CounterMsg;
45    type Output = CounterOutput;
46    type CommandOutput = ();
47    type Root = gtk::Box;
48    type Widgets = CounterWidgets;
49    type ParentWidget = gtk::Grid;
50    type Index = DynamicIndex;
51
52    fn init_root(&self) -> Self::Root {
53        relm4::view! {
54            root = gtk::Box {
55                set_orientation: gtk::Orientation::Horizontal,
56                set_spacing: 10,
57            }
58        }
59        root
60    }
61
62    fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
63        Self { value }
64    }
65
66    fn init_widgets(
67        &mut self,
68        index: &DynamicIndex,
69        root: Self::Root,
70        _returned_widget: &gtk::Widget,
71        sender: FactorySender<Self>,
72    ) -> Self::Widgets {
73        relm4::view! {
74            #[local_ref]
75            root -> gtk::Box {
76                #[name(label)]
77                gtk::Label {
78                    set_label: &self.value.to_string(),
79                    set_width_chars: 3,
80                },
81
82                gtk::Button {
83                    set_label: "+",
84                    connect_clicked => CounterMsg::Increment,
85                },
86
87                gtk::Button {
88                    set_label: "-",
89                    connect_clicked => CounterMsg::Decrement,
90                },
91
92                gtk::Button {
93                    set_label: "Up",
94                    connect_clicked[sender, index] => move |_| {
95                        sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
96                    }
97                },
98
99                gtk::Button {
100                    set_label: "Down",
101                    connect_clicked[sender, index] => move |_| {
102                        sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
103                    }
104                },
105
106                gtk::Button {
107                    set_label: "To Start",
108                    connect_clicked[sender, index] => move |_| {
109                        sender.output(CounterOutput::SendFront(index.clone())).unwrap();
110                    }
111                }
112            }
113        }
114
115        CounterWidgets { label }
116    }
117
118    fn update(&mut self, msg: Self::Input, _sender: FactorySender<Self>) {
119        match msg {
120            CounterMsg::Increment => {
121                self.value = self.value.wrapping_add(1);
122            }
123            CounterMsg::Decrement => {
124                self.value = self.value.wrapping_sub(1);
125            }
126        }
127    }
128
129    fn update_view(&self, widgets: &mut Self::Widgets, _sender: FactorySender<Self>) {
130        widgets.label.set_label(&self.value.to_string());
131    }
132}
133
134struct App {
135    created_widgets: u8,
136    counters: FactoryVecDeque<Counter>,
137}
138
139#[derive(Debug)]
140enum AppMsg {
141    AddCounter,
142    RemoveCounter,
143    SendFront(DynamicIndex),
144    MoveUp(DynamicIndex),
145    MoveDown(DynamicIndex),
146}
147
148#[relm4::component]
149impl SimpleComponent for App {
150    type Init = u8;
151    type Input = AppMsg;
152    type Output = ();
153
154    view! {
155        gtk::Window {
156            set_title: Some("Grid factory example"),
157            set_default_size: (300, 100),
158
159            gtk::Box {
160                set_orientation: gtk::Orientation::Vertical,
161                set_spacing: 5,
162                set_margin_all: 5,
163
164                gtk::Button {
165                    set_label: "Add counter",
166                    connect_clicked => AppMsg::AddCounter,
167                },
168
169                gtk::Button {
170                    set_label: "Remove counter",
171                    connect_clicked => AppMsg::RemoveCounter,
172                },
173
174                #[local_ref]
175                counter_grid -> gtk::Grid {
176                    set_orientation: gtk::Orientation::Vertical,
177                    set_column_spacing: 15,
178                    set_row_spacing: 5,
179                }
180            }
181        }
182    }
183
184    fn init(
185        counter: Self::Init,
186        root: Self::Root,
187        sender: ComponentSender<Self>,
188    ) -> ComponentParts<Self> {
189        let counters =
190            FactoryVecDeque::builder()
191                .launch_default()
192                .forward(sender.input_sender(), |msg| match msg {
193                    CounterOutput::SendFront(index) => AppMsg::SendFront(index),
194                    CounterOutput::MoveUp(index) => AppMsg::MoveUp(index),
195                    CounterOutput::MoveDown(index) => AppMsg::MoveDown(index),
196                });
197
198        let model = App {
199            created_widgets: counter,
200            counters,
201        };
202
203        let counter_grid = model.counters.widget();
204        let widgets = view_output!();
205
206        ComponentParts { model, widgets }
207    }
208
209    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
210        let mut counters_guard = self.counters.guard();
211
212        match msg {
213            AppMsg::AddCounter => {
214                counters_guard.push_back(self.created_widgets);
215                self.created_widgets = self.created_widgets.wrapping_add(1);
216            }
217            AppMsg::RemoveCounter => {
218                counters_guard.pop_back();
219            }
220            AppMsg::SendFront(index) => {
221                counters_guard.move_front(index.current_index());
222            }
223            AppMsg::MoveDown(index) => {
224                let index = index.current_index();
225                let new_index = index + 1;
226                // Already at the end?
227                if new_index < counters_guard.len() {
228                    counters_guard.move_to(index, new_index);
229                }
230            }
231            AppMsg::MoveUp(index) => {
232                let index = index.current_index();
233                // Already at the start?
234                if index != 0 {
235                    counters_guard.move_to(index, index - 1);
236                }
237            }
238        }
239    }
More examples
Hide additional examples
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    }
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/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    }
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    }
relm4/examples/factory_async.rs (line 224)
213    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
214        let mut counters_guard = self.counters.guard();
215        match msg {
216            AppMsg::AddCounter => {
217                counters_guard.push_back(self.created_widgets);
218                self.created_widgets = self.created_widgets.wrapping_add(1);
219            }
220            AppMsg::RemoveCounter => {
221                counters_guard.pop_back();
222            }
223            AppMsg::SendFront(index) => {
224                counters_guard.move_front(index.current_index());
225            }
226            AppMsg::MoveDown(index) => {
227                let index = index.current_index();
228                let new_index = index + 1;
229                // Already at the end?
230                if new_index < counters_guard.len() {
231                    counters_guard.move_to(index, new_index);
232                }
233            }
234            AppMsg::MoveUp(index) => {
235                let index = index.current_index();
236                // Already at the start?
237                if index != 0 {
238                    counters_guard.move_to(index, index - 1);
239                }
240            }
241            AppMsg::Remove(index) => {
242                counters_guard.remove(index.current_index());
243            }
244        }
245    }

Trait Implementations§

Source§

impl Clone for DynamicIndex

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DynamicIndex

Source§

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

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

impl PartialEq for DynamicIndex

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for DynamicIndex

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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