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(&mut self, _id: Option<&Id>, _bounds: Rectangle, _state: &mut dyn Focusable) {}
45
46    /// Operates on a widget that has text input.
47    fn text_input(&mut self, _id: Option<&Id>, _bounds: Rectangle, _state: &mut dyn TextInput) {}
48
49    /// Operates on a widget that contains some text.
50    fn text(&mut self, _id: Option<&Id>, _bounds: Rectangle, _text: &str) {}
51
52    /// Operates on a custom widget with some state.
53    fn custom(&mut self, _id: Option<&Id>, _bounds: Rectangle, _state: &mut dyn Any) {}
54
55    /// Finishes the [`Operation`] and returns its [`Outcome`].
56    fn finish(&self) -> Outcome<T> {
57        Outcome::None
58    }
59}
60
61impl<T, O> Operation<O> for Box<T>
62where
63    T: Operation<O> + ?Sized,
64{
65    fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<O>)) {
66        self.as_mut().traverse(operate);
67    }
68
69    fn container(&mut self, id: Option<&Id>, bounds: Rectangle) {
70        self.as_mut().container(id, bounds);
71    }
72
73    fn focusable(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Focusable) {
74        self.as_mut().focusable(id, bounds, state);
75    }
76
77    fn scrollable(
78        &mut self,
79        id: Option<&Id>,
80        bounds: Rectangle,
81        content_bounds: Rectangle,
82        translation: Vector,
83        state: &mut dyn Scrollable,
84    ) {
85        self.as_mut()
86            .scrollable(id, bounds, content_bounds, translation, state);
87    }
88
89    fn text_input(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn TextInput) {
90        self.as_mut().text_input(id, bounds, state);
91    }
92
93    fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
94        self.as_mut().text(id, bounds, text);
95    }
96
97    fn custom(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Any) {
98        self.as_mut().custom(id, bounds, state);
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>(operation: &'a mut dyn Operation<T>) -> impl Operation<O> + 'a
133where
134    T: 'a,
135{
136    struct BlackBox<'a, T> {
137        operation: &'a mut dyn Operation<T>,
138    }
139
140    impl<T, O> Operation<O> for BlackBox<'_, T> {
141        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<O>))
142        where
143            Self: Sized,
144        {
145            self.operation.traverse(&mut |operation| {
146                operate(&mut BlackBox { operation });
147            });
148        }
149
150        fn container(&mut self, id: Option<&Id>, bounds: Rectangle) {
151            self.operation.container(id, bounds);
152        }
153
154        fn focusable(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Focusable) {
155            self.operation.focusable(id, bounds, state);
156        }
157
158        fn scrollable(
159            &mut self,
160            id: Option<&Id>,
161            bounds: Rectangle,
162            content_bounds: Rectangle,
163            translation: Vector,
164            state: &mut dyn Scrollable,
165        ) {
166            self.operation
167                .scrollable(id, bounds, content_bounds, translation, state);
168        }
169
170        fn text_input(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn TextInput) {
171            self.operation.text_input(id, bounds, state);
172        }
173
174        fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
175            self.operation.text(id, bounds, text);
176        }
177
178        fn custom(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Any) {
179            self.operation.custom(id, bounds, state);
180        }
181
182        fn finish(&self) -> Outcome<O> {
183            Outcome::None
184        }
185    }
186
187    BlackBox { operation }
188}
189
190/// Maps the output of an [`Operation`] using the given function.
191pub fn map<A, B>(
192    operation: impl Operation<A>,
193    f: impl Fn(A) -> B + Send + Sync + 'static,
194) -> impl Operation<B>
195where
196    A: 'static,
197    B: 'static,
198{
199    struct Map<O, A, B> {
200        operation: O,
201        f: Arc<dyn Fn(A) -> B + Send + Sync>,
202    }
203
204    impl<O, A, B> Operation<B> for Map<O, A, B>
205    where
206        O: Operation<A>,
207        A: 'static,
208        B: 'static,
209    {
210        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<B>)) {
211            struct MapRef<'a, A> {
212                operation: &'a mut dyn Operation<A>,
213            }
214
215            impl<A, B> Operation<B> for MapRef<'_, A> {
216                fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<B>)) {
217                    self.operation.traverse(&mut |operation| {
218                        operate(&mut MapRef { operation });
219                    });
220                }
221
222                fn container(&mut self, id: Option<&Id>, bounds: Rectangle) {
223                    let Self { operation, .. } = self;
224
225                    operation.container(id, bounds);
226                }
227
228                fn scrollable(
229                    &mut self,
230                    id: Option<&Id>,
231                    bounds: Rectangle,
232                    content_bounds: Rectangle,
233                    translation: Vector,
234                    state: &mut dyn Scrollable,
235                ) {
236                    self.operation
237                        .scrollable(id, bounds, content_bounds, translation, state);
238                }
239
240                fn focusable(
241                    &mut self,
242                    id: Option<&Id>,
243                    bounds: Rectangle,
244                    state: &mut dyn Focusable,
245                ) {
246                    self.operation.focusable(id, bounds, state);
247                }
248
249                fn text_input(
250                    &mut self,
251                    id: Option<&Id>,
252                    bounds: Rectangle,
253                    state: &mut dyn TextInput,
254                ) {
255                    self.operation.text_input(id, bounds, state);
256                }
257
258                fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
259                    self.operation.text(id, bounds, text);
260                }
261
262                fn custom(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Any) {
263                    self.operation.custom(id, bounds, state);
264                }
265            }
266
267            self.operation.traverse(&mut |operation| {
268                operate(&mut MapRef { operation });
269            });
270        }
271
272        fn container(&mut self, id: Option<&Id>, bounds: Rectangle) {
273            self.operation.container(id, bounds);
274        }
275
276        fn focusable(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Focusable) {
277            self.operation.focusable(id, bounds, state);
278        }
279
280        fn scrollable(
281            &mut self,
282            id: Option<&Id>,
283            bounds: Rectangle,
284            content_bounds: Rectangle,
285            translation: Vector,
286            state: &mut dyn Scrollable,
287        ) {
288            self.operation
289                .scrollable(id, bounds, content_bounds, translation, state);
290        }
291
292        fn text_input(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn TextInput) {
293            self.operation.text_input(id, bounds, state);
294        }
295
296        fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
297            self.operation.text(id, bounds, text);
298        }
299
300        fn custom(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Any) {
301            self.operation.custom(id, bounds, state);
302        }
303
304        fn finish(&self) -> Outcome<B> {
305            match self.operation.finish() {
306                Outcome::None => Outcome::None,
307                Outcome::Some(output) => Outcome::Some((self.f)(output)),
308                Outcome::Chain(next) => Outcome::Chain(Box::new(Map {
309                    operation: next,
310                    f: self.f.clone(),
311                })),
312            }
313        }
314    }
315
316    Map {
317        operation,
318        f: Arc::new(f),
319    }
320}
321
322/// Chains the output of an [`Operation`] with the provided function to
323/// build a new [`Operation`].
324pub fn then<A, B, O>(operation: impl Operation<A> + 'static, f: fn(A) -> O) -> impl Operation<B>
325where
326    A: 'static,
327    B: Send + 'static,
328    O: Operation<B> + 'static,
329{
330    struct Chain<T, O, A, B>
331    where
332        T: Operation<A>,
333        O: Operation<B>,
334    {
335        operation: T,
336        next: fn(A) -> O,
337        _result: PhantomData<B>,
338    }
339
340    impl<T, O, A, B> Operation<B> for Chain<T, O, A, B>
341    where
342        T: Operation<A> + 'static,
343        O: Operation<B> + 'static,
344        A: 'static,
345        B: Send + 'static,
346    {
347        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<B>)) {
348            self.operation.traverse(&mut |operation| {
349                operate(&mut black_box(operation));
350            });
351        }
352
353        fn container(&mut self, id: Option<&Id>, bounds: Rectangle) {
354            self.operation.container(id, bounds);
355        }
356
357        fn focusable(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Focusable) {
358            self.operation.focusable(id, bounds, state);
359        }
360
361        fn scrollable(
362            &mut self,
363            id: Option<&Id>,
364            bounds: Rectangle,
365            content_bounds: Rectangle,
366            translation: crate::Vector,
367            state: &mut dyn Scrollable,
368        ) {
369            self.operation
370                .scrollable(id, bounds, content_bounds, translation, state);
371        }
372
373        fn text_input(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn TextInput) {
374            self.operation.text_input(id, bounds, state);
375        }
376
377        fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
378            self.operation.text(id, bounds, text);
379        }
380
381        fn custom(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Any) {
382            self.operation.custom(id, bounds, state);
383        }
384
385        fn finish(&self) -> Outcome<B> {
386            match self.operation.finish() {
387                Outcome::None => Outcome::None,
388                Outcome::Some(value) => Outcome::Chain(Box::new((self.next)(value))),
389                Outcome::Chain(operation) => Outcome::Chain(Box::new(then(operation, self.next))),
390            }
391        }
392    }
393
394    Chain {
395        operation,
396        next: f,
397        _result: PhantomData,
398    }
399}
400
401/// Produces an [`Operation`] that applies the given [`Operation`] to the
402/// children of a container with the given [`Id`].
403pub fn scope<T: 'static>(target: Id, operation: impl Operation<T> + 'static) -> impl Operation<T> {
404    struct ScopedOperation<Message> {
405        target: Id,
406        current: Option<Id>,
407        operation: Box<dyn Operation<Message>>,
408    }
409
410    impl<Message: 'static> Operation<Message> for ScopedOperation<Message> {
411        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<Message>)) {
412            if self.current.as_ref() == Some(&self.target) {
413                self.operation.as_mut().traverse(operate);
414            } else {
415                operate(self);
416            }
417
418            self.current = None;
419        }
420
421        fn container(&mut self, id: Option<&Id>, _bounds: Rectangle) {
422            self.current = id.cloned();
423        }
424
425        fn finish(&self) -> Outcome<Message> {
426            match self.operation.finish() {
427                Outcome::Chain(next) => Outcome::Chain(Box::new(ScopedOperation {
428                    target: self.target.clone(),
429                    current: None,
430                    operation: next,
431                })),
432                outcome => outcome,
433            }
434        }
435    }
436
437    ScopedOperation {
438        target,
439        current: None,
440        operation: Box::new(operation),
441    }
442}