Skip to main content

iced_core/widget/
operation.rs

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