Skip to main content

iced_core/widget/
operation.rs

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