iced_core/widget/
operation.rs

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