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