iced_core/widget/
operation.rs

1//! Query or update internal widget state.
2pub mod focusable;
3pub mod scrollable;
4pub mod search_id;
5pub mod text_input;
6
7pub use focusable::Focusable;
8pub use scrollable::Scrollable;
9pub use text_input::TextInput;
10
11use crate::widget::Id;
12use crate::{Rectangle, Vector};
13
14use std::any::Any;
15use std::fmt;
16use std::marker::PhantomData;
17use std::sync::Arc;
18
19/// A piece of logic that can traverse the widget tree of an application in
20/// order to query or update some widget state.
21pub trait Operation<T = ()>: Send {
22    /// Operates on a widget that contains other widgets.
23    ///
24    /// The `operate_on_children` function can be called to return control to
25    /// the widget tree and keep traversing it.
26    fn container(
27        &mut self,
28        id: Option<&Id>,
29        bounds: Rectangle,
30        operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
31    );
32
33    /// Operates on a widget that can be focused.
34    fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {}
35
36    /// Operates on a widget that can be scrolled.
37    fn scrollable(
38        &mut self,
39        _state: &mut dyn Scrollable,
40        _id: Option<&Id>,
41        _bounds: Rectangle,
42        _content_bounds: Rectangle,
43        _translation: Vector,
44    ) {
45    }
46
47    /// Operates on a widget that has text input.
48    fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
49
50    /// Operates on a custom widget.
51    fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {}
52
53    /// Finishes the [`Operation`] and returns its [`Outcome`].
54    fn finish(&self) -> Outcome<T> {
55        Outcome::None
56    }
57}
58
59impl<T, O> Operation<O> for Box<T>
60where
61    T: Operation<O> + ?Sized,
62{
63    fn container(
64        &mut self,
65        id: Option<&Id>,
66        bounds: Rectangle,
67        operate_on_children: &mut dyn FnMut(&mut dyn Operation<O>),
68    ) {
69        self.as_mut().container(id, bounds, operate_on_children);
70    }
71
72    fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
73        self.as_mut().focusable(state, id);
74    }
75
76    fn scrollable(
77        &mut self,
78        state: &mut dyn Scrollable,
79        id: Option<&Id>,
80        bounds: Rectangle,
81        content_bounds: Rectangle,
82        translation: Vector,
83    ) {
84        self.as_mut().scrollable(
85            state,
86            id,
87            bounds,
88            content_bounds,
89            translation,
90        );
91    }
92
93    fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
94        self.as_mut().text_input(state, id);
95    }
96
97    fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
98        self.as_mut().custom(state, id);
99    }
100
101    fn finish(&self) -> Outcome<O> {
102        self.as_ref().finish()
103    }
104}
105
106/// The result of an [`Operation`].
107pub enum Outcome<T> {
108    /// The [`Operation`] produced no result.
109    None,
110
111    /// The [`Operation`] produced some result.
112    Some(T),
113
114    /// The [`Operation`] needs to be followed by another [`Operation`].
115    Chain(Box<dyn Operation<T>>),
116}
117
118impl<T> fmt::Debug for Outcome<T>
119where
120    T: fmt::Debug,
121{
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        match self {
124            Self::None => write!(f, "Outcome::None"),
125            Self::Some(output) => write!(f, "Outcome::Some({output:?})"),
126            Self::Chain(_) => write!(f, "Outcome::Chain(...)"),
127        }
128    }
129}
130
131/// Wraps the [`Operation`] in a black box, erasing its returning type.
132pub fn black_box<'a, T, O>(
133    operation: &'a mut dyn Operation<T>,
134) -> impl Operation<O> + 'a
135where
136    T: 'a,
137{
138    struct BlackBox<'a, T> {
139        operation: &'a mut dyn Operation<T>,
140    }
141
142    impl<'a, T, O> Operation<O> for BlackBox<'a, T> {
143        fn container(
144            &mut self,
145            id: Option<&Id>,
146            bounds: Rectangle,
147            operate_on_children: &mut dyn FnMut(&mut dyn Operation<O>),
148        ) {
149            self.operation.container(id, bounds, &mut |operation| {
150                operate_on_children(&mut BlackBox { operation });
151            });
152        }
153
154        fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
155            self.operation.focusable(state, id);
156        }
157
158        fn scrollable(
159            &mut self,
160            state: &mut dyn Scrollable,
161            id: Option<&Id>,
162            bounds: Rectangle,
163            content_bounds: Rectangle,
164            translation: Vector,
165        ) {
166            self.operation.scrollable(
167                state,
168                id,
169                bounds,
170                content_bounds,
171                translation,
172            );
173        }
174
175        fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
176            self.operation.text_input(state, id);
177        }
178
179        fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
180            self.operation.custom(state, id);
181        }
182
183        fn finish(&self) -> Outcome<O> {
184            Outcome::None
185        }
186    }
187
188    BlackBox { operation }
189}
190
191/// Maps the output of an [`Operation`] using the given function.
192pub fn map<A, B>(
193    operation: impl Operation<A>,
194    f: impl Fn(A) -> B + Send + Sync + 'static,
195) -> impl Operation<B>
196where
197    A: 'static,
198    B: 'static,
199{
200    #[allow(missing_debug_implementations)]
201    struct Map<O, A, B> {
202        operation: O,
203        f: Arc<dyn Fn(A) -> B + Send + Sync>,
204    }
205
206    impl<O, A, B> Operation<B> for Map<O, A, B>
207    where
208        O: Operation<A>,
209        A: 'static,
210        B: 'static,
211    {
212        fn container(
213            &mut self,
214            id: Option<&Id>,
215            bounds: Rectangle,
216            operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
217        ) {
218            struct MapRef<'a, A> {
219                operation: &'a mut dyn Operation<A>,
220            }
221
222            impl<'a, A, B> Operation<B> for MapRef<'a, A> {
223                fn container(
224                    &mut self,
225                    id: Option<&Id>,
226                    bounds: Rectangle,
227                    operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
228                ) {
229                    let Self { operation, .. } = self;
230
231                    operation.container(id, bounds, &mut |operation| {
232                        operate_on_children(&mut MapRef { operation });
233                    });
234                }
235
236                fn scrollable(
237                    &mut self,
238                    state: &mut dyn Scrollable,
239                    id: Option<&Id>,
240                    bounds: Rectangle,
241                    content_bounds: Rectangle,
242                    translation: Vector,
243                ) {
244                    self.operation.scrollable(
245                        state,
246                        id,
247                        bounds,
248                        content_bounds,
249                        translation,
250                    );
251                }
252
253                fn focusable(
254                    &mut self,
255                    state: &mut dyn Focusable,
256                    id: Option<&Id>,
257                ) {
258                    self.operation.focusable(state, id);
259                }
260
261                fn text_input(
262                    &mut self,
263                    state: &mut dyn TextInput,
264                    id: Option<&Id>,
265                ) {
266                    self.operation.text_input(state, id);
267                }
268
269                fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
270                    self.operation.custom(state, id);
271                }
272            }
273
274            let Self { operation, .. } = self;
275
276            MapRef { operation }.container(id, bounds, operate_on_children);
277        }
278
279        fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
280            self.operation.focusable(state, id);
281        }
282
283        fn scrollable(
284            &mut self,
285            state: &mut dyn Scrollable,
286            id: Option<&Id>,
287            bounds: Rectangle,
288            content_bounds: Rectangle,
289            translation: Vector,
290        ) {
291            self.operation.scrollable(
292                state,
293                id,
294                bounds,
295                content_bounds,
296                translation,
297            );
298        }
299
300        fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
301            self.operation.text_input(state, id);
302        }
303
304        fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
305            self.operation.custom(state, id);
306        }
307
308        fn finish(&self) -> Outcome<B> {
309            match self.operation.finish() {
310                Outcome::None => Outcome::None,
311                Outcome::Some(output) => Outcome::Some((self.f)(output)),
312                Outcome::Chain(next) => Outcome::Chain(Box::new(Map {
313                    operation: next,
314                    f: self.f.clone(),
315                })),
316            }
317        }
318    }
319
320    Map {
321        operation,
322        f: Arc::new(f),
323    }
324}
325
326/// Chains the output of an [`Operation`] with the provided function to
327/// build a new [`Operation`].
328pub fn then<A, B, O>(
329    operation: impl Operation<A> + 'static,
330    f: fn(A) -> O,
331) -> impl Operation<B>
332where
333    A: 'static,
334    B: Send + 'static,
335    O: Operation<B> + 'static,
336{
337    struct Chain<T, O, A, B>
338    where
339        T: Operation<A>,
340        O: Operation<B>,
341    {
342        operation: T,
343        next: fn(A) -> O,
344        _result: PhantomData<B>,
345    }
346
347    impl<T, O, A, B> Operation<B> for Chain<T, O, A, B>
348    where
349        T: Operation<A> + 'static,
350        O: Operation<B> + 'static,
351        A: 'static,
352        B: Send + 'static,
353    {
354        fn container(
355            &mut self,
356            id: Option<&Id>,
357            bounds: Rectangle,
358            operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
359        ) {
360            self.operation.container(id, bounds, &mut |operation| {
361                operate_on_children(&mut black_box(operation));
362            });
363        }
364
365        fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
366            self.operation.focusable(state, id);
367        }
368
369        fn scrollable(
370            &mut self,
371            state: &mut dyn Scrollable,
372            id: Option<&Id>,
373            bounds: Rectangle,
374            content_bounds: Rectangle,
375            translation: crate::Vector,
376        ) {
377            self.operation.scrollable(
378                state,
379                id,
380                bounds,
381                content_bounds,
382                translation,
383            );
384        }
385
386        fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
387            self.operation.text_input(state, id);
388        }
389
390        fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) {
391            self.operation.custom(state, id);
392        }
393
394        fn finish(&self) -> Outcome<B> {
395            match self.operation.finish() {
396                Outcome::None => Outcome::None,
397                Outcome::Some(value) => {
398                    Outcome::Chain(Box::new((self.next)(value)))
399                }
400                Outcome::Chain(operation) => {
401                    Outcome::Chain(Box::new(then(operation, self.next)))
402                }
403            }
404        }
405    }
406
407    Chain {
408        operation,
409        next: f,
410        _result: PhantomData,
411    }
412}
413
414/// Produces an [`Operation`] that applies the given [`Operation`] to the
415/// children of a container with the given [`Id`].
416pub fn scoped<T: 'static>(
417    target: Id,
418    operation: impl Operation<T> + 'static,
419) -> impl Operation<T> {
420    struct ScopedOperation<Message> {
421        target: Id,
422        operation: Box<dyn Operation<Message>>,
423    }
424
425    impl<Message: 'static> Operation<Message> for ScopedOperation<Message> {
426        fn container(
427            &mut self,
428            id: Option<&Id>,
429            _bounds: Rectangle,
430            operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>),
431        ) {
432            if id == Some(&self.target) {
433                operate_on_children(self.operation.as_mut());
434            } else {
435                operate_on_children(self);
436            }
437        }
438
439        fn finish(&self) -> Outcome<Message> {
440            match self.operation.finish() {
441                Outcome::Chain(next) => {
442                    Outcome::Chain(Box::new(ScopedOperation {
443                        target: self.target.clone(),
444                        operation: next,
445                    }))
446                }
447                outcome => outcome,
448            }
449        }
450    }
451
452    ScopedOperation {
453        target,
454        operation: Box::new(operation),
455    }
456}