diff options
| author | 2024-08-08 01:25:00 +0200 | |
|---|---|---|
| committer | 2024-08-08 01:25:00 +0200 | |
| commit | 422568dee49fa6b814ae0131a3f88d4ae2be243b (patch) | |
| tree | 39e458a2dd545b90f81d3066c29a74ec27dec505 /core | |
| parent | d5ffe98ce939990e6c8ef6f40895cd1f9633fe18 (diff) | |
| download | iced-422568dee49fa6b814ae0131a3f88d4ae2be243b.tar.gz iced-422568dee49fa6b814ae0131a3f88d4ae2be243b.tar.bz2 iced-422568dee49fa6b814ae0131a3f88d4ae2be243b.zip | |
Introduce `black_box` and `chain` in `widget::operation`
Diffstat (limited to '')
| -rw-r--r-- | core/src/element.rs | 4 | ||||
| -rw-r--r-- | core/src/overlay.rs | 2 | ||||
| -rw-r--r-- | core/src/overlay/element.rs | 4 | ||||
| -rw-r--r-- | core/src/overlay/group.rs | 2 | ||||
| -rw-r--r-- | core/src/widget.rs | 2 | ||||
| -rw-r--r-- | core/src/widget/operation.rs | 191 | ||||
| -rw-r--r-- | core/src/widget/operation/focusable.rs | 30 | 
7 files changed, 200 insertions, 35 deletions
| diff --git a/core/src/element.rs b/core/src/element.rs index 385d8295..6ebb8a15 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -304,7 +304,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<()>, +        operation: &mut dyn widget::Operation,      ) {          self.widget.operate(tree, layout, renderer, operation);      } @@ -440,7 +440,7 @@ where          state: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<()>, +        operation: &mut dyn widget::Operation,      ) {          self.element              .widget diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 3b79970e..f09de831 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -41,7 +41,7 @@ where          &mut self,          _layout: Layout<'_>,          _renderer: &Renderer, -        _operation: &mut dyn widget::Operation<()>, +        _operation: &mut dyn widget::Operation,      ) {      } diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index 61e75e8a..32e987a3 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -92,7 +92,7 @@ where          &mut self,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<()>, +        operation: &mut dyn widget::Operation,      ) {          self.overlay.operate(layout, renderer, operation);      } @@ -144,7 +144,7 @@ where          &mut self,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<()>, +        operation: &mut dyn widget::Operation,      ) {          self.content.operate(layout, renderer, operation);      } diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index cd12eac9..6541d311 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -132,7 +132,7 @@ where          &mut self,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<()>, +        operation: &mut dyn widget::Operation,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.children.iter_mut().zip(layout.children()).for_each( diff --git a/core/src/widget.rs b/core/src/widget.rs index 08cfa55b..c5beea54 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -105,7 +105,7 @@ where          _state: &mut Tree,          _layout: Layout<'_>,          _renderer: &Renderer, -        _operation: &mut dyn Operation<()>, +        _operation: &mut dyn Operation,      ) {      } diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs index 3e4ed618..741e1a5f 100644 --- a/core/src/widget/operation.rs +++ b/core/src/widget/operation.rs @@ -12,11 +12,12 @@ use crate::{Rectangle, Vector};  use std::any::Any;  use std::fmt; +use std::marker::PhantomData;  use std::sync::Arc;  /// A piece of logic that can traverse the widget tree of an application in  /// order to query or update some widget state. -pub trait Operation<T>: Send { +pub trait Operation<T = ()>: Send {      /// Operates on a widget that contains other widgets.      ///      /// The `operate_on_children` function can be called to return control to @@ -53,6 +54,46 @@ pub trait Operation<T>: Send {      }  } +impl<T, O> Operation<O> for Box<T> +where +    T: Operation<O> + ?Sized, +{ +    fn container( +        &mut self, +        id: Option<&Id>, +        bounds: Rectangle, +        operate_on_children: &mut dyn FnMut(&mut dyn Operation<O>), +    ) { +        self.as_mut().container(id, bounds, operate_on_children); +    } + +    fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { +        self.as_mut().focusable(state, id); +    } + +    fn scrollable( +        &mut self, +        state: &mut dyn Scrollable, +        id: Option<&Id>, +        bounds: Rectangle, +        translation: Vector, +    ) { +        self.as_mut().scrollable(state, id, bounds, translation); +    } + +    fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { +        self.as_mut().text_input(state, id); +    } + +    fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { +        self.as_mut().custom(state, id); +    } + +    fn finish(&self) -> Outcome<O> { +        self.as_ref().finish() +    } +} +  /// The result of an [`Operation`].  pub enum Outcome<T> {      /// The [`Operation`] produced no result. @@ -78,9 +119,62 @@ where      }  } +/// Wraps the [`Operation`] in a black box, erasing its returning type. +pub fn black_box<'a, T, O>( +    operation: &'a mut dyn Operation<T>, +) -> impl Operation<O> + 'a +where +    T: 'a, +{ +    struct BlackBox<'a, T> { +        operation: &'a mut dyn Operation<T>, +    } + +    impl<'a, T, O> Operation<O> for BlackBox<'a, T> { +        fn container( +            &mut self, +            id: Option<&Id>, +            bounds: Rectangle, +            operate_on_children: &mut dyn FnMut(&mut dyn Operation<O>), +        ) { +            self.operation.container(id, bounds, &mut |operation| { +                operate_on_children(&mut BlackBox { operation }); +            }); +        } + +        fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { +            self.operation.focusable(state, id); +        } + +        fn scrollable( +            &mut self, +            state: &mut dyn Scrollable, +            id: Option<&Id>, +            bounds: Rectangle, +            translation: Vector, +        ) { +            self.operation.scrollable(state, id, bounds, translation); +        } + +        fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { +            self.operation.text_input(state, id); +        } + +        fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { +            self.operation.custom(state, id); +        } + +        fn finish(&self) -> Outcome<O> { +            Outcome::None +        } +    } + +    BlackBox { operation } +} +  /// Maps the output of an [`Operation`] using the given function.  pub fn map<A, B>( -    operation: Box<dyn Operation<A>>, +    operation: impl Operation<A>,      f: impl Fn(A) -> B + Send + Sync + 'static,  ) -> impl Operation<B>  where @@ -88,13 +182,14 @@ where      B: 'static,  {      #[allow(missing_debug_implementations)] -    struct Map<A, B> { -        operation: Box<dyn Operation<A>>, +    struct Map<O, A, B> { +        operation: O,          f: Arc<dyn Fn(A) -> B + Send + Sync>,      } -    impl<A, B> Operation<B> for Map<A, B> +    impl<O, A, B> Operation<B> for Map<O, A, B>      where +        O: Operation<A>,          A: 'static,          B: 'static,      { @@ -155,10 +250,7 @@ where              let Self { operation, .. } = self; -            MapRef { -                operation: operation.as_mut(), -            } -            .container(id, bounds, operate_on_children); +            MapRef { operation }.container(id, bounds, operate_on_children);          }          fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { @@ -201,6 +293,87 @@ where      }  } +/// Chains the output of an [`Operation`] with the provided function to +/// build a new [`Operation`]. +pub fn chain<A, B, O>( +    operation: impl Operation<A> + 'static, +    f: fn(A) -> O, +) -> impl Operation<B> +where +    A: 'static, +    B: Send + 'static, +    O: Operation<B> + 'static, +{ +    struct Chain<T, O, A, B> +    where +        T: Operation<A>, +        O: Operation<B>, +    { +        operation: T, +        next: fn(A) -> O, +        _result: PhantomData<B>, +    } + +    impl<T, O, A, B> Operation<B> for Chain<T, O, A, B> +    where +        T: Operation<A> + 'static, +        O: Operation<B> + 'static, +        A: 'static, +        B: Send + 'static, +    { +        fn container( +            &mut self, +            id: Option<&Id>, +            bounds: Rectangle, +            operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>), +        ) { +            self.operation.container(id, bounds, &mut |operation| { +                operate_on_children(&mut black_box(operation)); +            }); +        } + +        fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { +            self.operation.focusable(state, id); +        } + +        fn scrollable( +            &mut self, +            state: &mut dyn Scrollable, +            id: Option<&Id>, +            bounds: Rectangle, +            translation: crate::Vector, +        ) { +            self.operation.scrollable(state, id, bounds, translation); +        } + +        fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { +            self.operation.text_input(state, id); +        } + +        fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) { +            self.operation.custom(state, id); +        } + +        fn finish(&self) -> Outcome<B> { +            match self.operation.finish() { +                Outcome::None => Outcome::None, +                Outcome::Some(value) => { +                    Outcome::Chain(Box::new((self.next)(value))) +                } +                Outcome::Chain(operation) => { +                    Outcome::Chain(Box::new(chain(operation, self.next))) +                } +            } +        } +    } + +    Chain { +        operation, +        next: f, +        _result: PhantomData, +    } +} +  /// Produces an [`Operation`] that applies the given [`Operation`] to the  /// children of a container with the given [`Id`].  pub fn scope<T: 'static>( diff --git a/core/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs index 68c22faa..0a6f2e96 100644 --- a/core/src/widget/operation/focusable.rs +++ b/core/src/widget/operation/focusable.rs @@ -1,5 +1,5 @@  //! Operate on widgets that can be focused. -use crate::widget::operation::{Operation, Outcome}; +use crate::widget::operation::{self, Operation, Outcome};  use crate::widget::Id;  use crate::Rectangle; @@ -58,19 +58,12 @@ pub fn focus<T>(target: Id) -> impl Operation<T> {  /// Produces an [`Operation`] that generates a [`Count`] and chains it with the  /// provided function to build a new [`Operation`]. -pub fn count<T, O>(f: fn(Count) -> O) -> impl Operation<T> -where -    O: Operation<T> + 'static, -{ -    struct CountFocusable<O> { +pub fn count() -> impl Operation<Count> { +    struct CountFocusable {          count: Count, -        next: fn(Count) -> O,      } -    impl<T, O> Operation<T> for CountFocusable<O> -    where -        O: Operation<T> + 'static, -    { +    impl Operation<Count> for CountFocusable {          fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) {              if state.is_focused() {                  self.count.focused = Some(self.count.total); @@ -83,26 +76,25 @@ where              &mut self,              _id: Option<&Id>,              _bounds: Rectangle, -            operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), +            operate_on_children: &mut dyn FnMut(&mut dyn Operation<Count>),          ) {              operate_on_children(self);          } -        fn finish(&self) -> Outcome<T> { -            Outcome::Chain(Box::new((self.next)(self.count))) +        fn finish(&self) -> Outcome<Count> { +            Outcome::Some(self.count)          }      }      CountFocusable {          count: Count::default(), -        next: f,      }  }  /// Produces an [`Operation`] that searches for the current focused widget, and  /// - if found, focuses the previous focusable widget.  /// - if not found, focuses the last focusable widget. -pub fn focus_previous<T>() -> impl Operation<T> { +pub fn focus_previous() -> impl Operation {      struct FocusPrevious {          count: Count,          current: usize, @@ -136,13 +128,13 @@ pub fn focus_previous<T>() -> impl Operation<T> {          }      } -    count(|count| FocusPrevious { count, current: 0 }) +    operation::chain(count(), |count| FocusPrevious { count, current: 0 })  }  /// Produces an [`Operation`] that searches for the current focused widget, and  /// - if found, focuses the next focusable widget.  /// - if not found, focuses the first focusable widget. -pub fn focus_next<T>() -> impl Operation<T> { +pub fn focus_next() -> impl Operation {      struct FocusNext {          count: Count,          current: usize, @@ -170,7 +162,7 @@ pub fn focus_next<T>() -> impl Operation<T> {          }      } -    count(|count| FocusNext { count, current: 0 }) +    operation::chain(count(), |count| FocusNext { count, current: 0 })  }  /// Produces an [`Operation`] that searches for the current focused widget | 
