diff options
Diffstat (limited to 'native')
| -rw-r--r-- | native/src/command.rs | 10 | ||||
| -rw-r--r-- | native/src/command/action.rs | 7 | ||||
| -rw-r--r-- | native/src/lib.rs | 4 | ||||
| -rw-r--r-- | native/src/overlay.rs | 9 | ||||
| -rw-r--r-- | native/src/overlay/element.rs | 10 | ||||
| -rw-r--r-- | native/src/user_interface.rs | 21 | ||||
| -rw-r--r-- | native/src/widget.rs | 17 | ||||
| -rw-r--r-- | native/src/widget/action.rs | 78 | ||||
| -rw-r--r-- | native/src/widget/helpers.rs | 4 | ||||
| -rw-r--r-- | native/src/widget/id.rs | 38 | ||||
| -rw-r--r-- | native/src/widget/operation.rs | 62 | ||||
| -rw-r--r-- | native/src/widget/state.rs | 5 | ||||
| -rw-r--r-- | native/src/widget/text_input.rs | 15 | 
13 files changed, 276 insertions, 4 deletions
diff --git a/native/src/command.rs b/native/src/command.rs index 89d0f045..b0b12805 100644 --- a/native/src/command.rs +++ b/native/src/command.rs @@ -3,6 +3,8 @@ mod action;  pub use action::Action; +use crate::widget; +  use iced_futures::MaybeSend;  use std::fmt; @@ -24,6 +26,13 @@ impl<T> Command<T> {          Self(iced_futures::Command::single(action))      } +    /// Creates a [`Command`] that performs a [`widget::Operation`]. +    pub fn widget(operation: impl widget::Operation<T> + 'static) -> Self { +        Self(iced_futures::Command::single(Action::Widget( +            widget::Action::new(operation), +        ))) +    } +      /// Creates a [`Command`] that performs the action of the given future.      pub fn perform<A>(          future: impl Future<Output = T> + 'static + MaybeSend, @@ -51,6 +60,7 @@ impl<T> Command<T> {      ) -> Command<A>      where          T: 'static, +        A: 'static,      {          let Command(command) = self; diff --git a/native/src/command/action.rs b/native/src/command/action.rs index 1bb03cef..3fb02899 100644 --- a/native/src/command/action.rs +++ b/native/src/command/action.rs @@ -1,5 +1,6 @@  use crate::clipboard;  use crate::system; +use crate::widget;  use crate::window;  use iced_futures::MaybeSend; @@ -23,6 +24,9 @@ pub enum Action<T> {      /// Run a system action.      System(system::Action<T>), + +    /// Run a widget action. +    Widget(widget::Action<T>),  }  impl<T> Action<T> { @@ -34,6 +38,7 @@ impl<T> Action<T> {          f: impl Fn(T) -> A + 'static + MaybeSend + Sync,      ) -> Action<A>      where +        A: 'static,          T: 'static,      {          use iced_futures::futures::FutureExt; @@ -43,6 +48,7 @@ impl<T> Action<T> {              Self::Clipboard(action) => Action::Clipboard(action.map(f)),              Self::Window(window) => Action::Window(window),              Self::System(system) => Action::System(system.map(f)), +            Self::Widget(widget) => Action::Widget(widget.map(f)),          }      }  } @@ -56,6 +62,7 @@ impl<T> fmt::Debug for Action<T> {              }              Self::Window(action) => write!(f, "Action::Window({:?})", action),              Self::System(action) => write!(f, "Action::System({:?})", action), +            Self::Widget(_action) => write!(f, "Action::Widget"),          }      }  } diff --git a/native/src/lib.rs b/native/src/lib.rs index 13173901..73a6c624 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -32,8 +32,8 @@      html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"  )]  #![deny( -    missing_debug_implementations, -    missing_docs, +//    missing_debug_implementations, +//    missing_docs,      unused_results,      clippy::extra_unused_lifetimes,      clippy::from_over_into, diff --git a/native/src/overlay.rs b/native/src/overlay.rs index c2a98693..905d3389 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -10,6 +10,7 @@ use crate::event::{self, Event};  use crate::layout;  use crate::mouse;  use crate::renderer; +use crate::widget;  use crate::widget::tree::{self, Tree};  use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size}; @@ -63,6 +64,14 @@ where      /// Reconciliates the [`Widget`] with the provided [`Tree`].      fn diff(&self, _tree: &mut Tree) {} +    /// Applies an [`Operation`] to the [`Widget`]. +    fn operate( +        &self, +        _layout: Layout<'_>, +        _operation: &mut dyn widget::Operation<Message>, +    ) { +    } +      /// Processes a runtime [`Event`].      ///      /// It receives: diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index de2e1f37..b919c221 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -4,6 +4,7 @@ use crate::event::{self, Event};  use crate::layout;  use crate::mouse;  use crate::renderer; +use crate::widget;  use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector};  /// A generic [`Overlay`]. @@ -102,6 +103,15 @@ where          self.overlay              .draw(renderer, theme, style, layout, cursor_position)      } + +    /// Applies an [`Operation`] to the [`Element`]. +    pub fn operate( +        &self, +        layout: Layout<'_>, +        operation: &mut dyn widget::Operation<Message>, +    ) { +        self.overlay.operate(layout, operation); +    }  }  struct Map<'a, A, B, Renderer> { diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 9f3a8e21..25557240 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -479,6 +479,27 @@ where              .unwrap_or(base_interaction)      } +    /// Applies a [`widget::Operation`] to the [`UserInterface`]. +    pub fn operate( +        &mut self, +        renderer: &Renderer, +        operation: &mut dyn widget::Operation<Message>, +    ) { +        self.root +            .as_widget() +            .operate(Layout::new(&self.base), operation); + +        if let Some(layout) = self.overlay.as_ref() { +            if let Some(overlay) = self.root.as_widget().overlay( +                &mut self.state, +                Layout::new(&self.base), +                renderer, +            ) { +                overlay.operate(Layout::new(layout), operation); +            } +        } +    } +      /// Relayouts and returns a new  [`UserInterface`] using the provided      /// bounds.      pub fn relayout(self, bounds: Size, renderer: &mut Renderer) -> Self { diff --git a/native/src/widget.rs b/native/src/widget.rs index 9a4f373a..79f6ae3a 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -17,6 +17,7 @@ pub mod column;  pub mod container;  pub mod helpers;  pub mod image; +pub mod operation;  pub mod pane_grid;  pub mod pick_list;  pub mod progress_bar; @@ -26,6 +27,7 @@ pub mod rule;  pub mod scrollable;  pub mod slider;  pub mod space; +pub mod state;  pub mod svg;  pub mod text;  pub mod text_input; @@ -33,6 +35,9 @@ pub mod toggler;  pub mod tooltip;  pub mod tree; +mod action; +mod id; +  #[doc(no_inline)]  pub use button::Button;  #[doc(no_inline)] @@ -76,6 +81,10 @@ pub use tooltip::Tooltip;  #[doc(no_inline)]  pub use tree::Tree; +pub use action::Action; +pub use id::Id; +pub use operation::Operation; +  use crate::event::{self, Event};  use crate::layout;  use crate::mouse; @@ -159,6 +168,14 @@ where      /// Reconciliates the [`Widget`] with the provided [`Tree`].      fn diff(&self, _tree: &mut Tree) {} +    /// Applies an [`Operation`] to the [`Widget`]. +    fn operate( +        &self, +        _layout: Layout<'_>, +        _operation: &mut dyn Operation<Message>, +    ) { +    } +      /// Processes a runtime [`Event`].      ///      /// By default, it does nothing. diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs new file mode 100644 index 00000000..23ea4269 --- /dev/null +++ b/native/src/widget/action.rs @@ -0,0 +1,78 @@ +use crate::widget::state; +use crate::widget::{Id, Operation}; + +use iced_futures::MaybeSend; + +pub struct Action<T>(Box<dyn Operation<T>>); + +impl<T> Action<T> { +    pub fn new(operation: impl Operation<T> + 'static) -> Self { +        Self(Box::new(operation)) +    } + +    pub fn map<A>( +        self, +        f: impl Fn(T) -> A + 'static + MaybeSend + Sync, +    ) -> Action<A> +    where +        T: 'static, +        A: 'static, +    { +        Action(Box::new(Map { +            operation: self.0, +            f: Box::new(f), +        })) +    } + +    pub fn into_operation(self) -> Box<dyn Operation<T>> { +        self.0 +    } +} + +struct Map<A, B> { +    operation: Box<dyn Operation<A>>, +    f: Box<dyn Fn(A) -> B>, +} + +impl<A, B> Operation<B> for Map<A, B> +where +    A: 'static, +    B: 'static, +{ +    fn container( +        &mut self, +        id: Option<&Id>, +        operate_on_children: &dyn Fn(&mut dyn Operation<B>), +    ) { +        struct MapRef<'a, A, B> { +            operation: &'a mut dyn Operation<A>, +            f: &'a dyn Fn(A) -> B, +        } + +        impl<'a, A, B> Operation<B> for MapRef<'a, A, B> { +            fn container( +                &mut self, +                id: Option<&Id>, +                operate_on_children: &dyn Fn(&mut dyn Operation<B>), +            ) { +                let Self { operation, f } = self; + +                operation.container(id, &|operation| { +                    operate_on_children(&mut MapRef { operation, f }); +                }); +            } +        } + +        let Self { operation, f } = self; + +        MapRef { +            operation: operation.as_mut(), +            f, +        } +        .container(id, operate_on_children); +    } + +    fn focusable(&mut self, state: &mut dyn state::Focusable, id: Option<&Id>) { +        self.operation.focusable(state, id); +    } +} diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index 518ac23b..a62448e9 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -49,8 +49,8 @@ where  /// [`Column`]: widget::Column  pub fn column<Message, Renderer>(      children: Vec<Element<'_, Message, Renderer>>, -) -> widget::Row<'_, Message, Renderer> { -    widget::Row::with_children(children) +) -> widget::Column<'_, Message, Renderer> { +    widget::Column::with_children(children)  }  /// Creates a new [`Row`] with the given children. diff --git a/native/src/widget/id.rs b/native/src/widget/id.rs new file mode 100644 index 00000000..4c0ab999 --- /dev/null +++ b/native/src/widget/id.rs @@ -0,0 +1,38 @@ +use std::borrow; +use std::sync::atomic::{self, AtomicUsize}; + +static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Id(Internal); + +impl Id { +    pub fn new(id: impl Into<borrow::Cow<'static, str>>) -> Self { +        Self(Internal::Custom(id.into())) +    } + +    pub fn unique() -> Self { +        let id = NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed); + +        Self(Internal::Unique(id)) +    } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Internal { +    Unique(usize), +    Custom(borrow::Cow<'static, str>), +} + +#[cfg(test)] +mod tests { +    use super::Id; + +    #[test] +    fn unique_generates_different_ids() { +        let a = Id::unique(); +        let b = Id::unique(); + +        assert_ne!(a, b); +    } +} diff --git a/native/src/widget/operation.rs b/native/src/widget/operation.rs new file mode 100644 index 00000000..b6c108e0 --- /dev/null +++ b/native/src/widget/operation.rs @@ -0,0 +1,62 @@ +use crate::widget::state; +use crate::widget::Id; + +pub trait Operation<T> { +    fn container( +        &mut self, +        id: Option<&Id>, +        operate_on_children: &dyn Fn(&mut dyn Operation<T>), +    ); + +    fn focusable( +        &mut self, +        _state: &mut dyn state::Focusable, +        _id: Option<&Id>, +    ) { +    } + +    fn finish(&self) -> Outcome<T> { +        Outcome::None +    } +} + +pub enum Outcome<T> { +    None, +    Some(T), +    Chain(Box<dyn Operation<T>>), +} + +pub fn focus<T>(target: Id) -> impl Operation<T> { +    struct Focus { +        target: Id, +    } + +    impl<T> Operation<T> for Focus { +        fn focusable( +            &mut self, +            state: &mut dyn state::Focusable, +            id: Option<&Id>, +        ) { +            if state.is_focused() { +                match id { +                    Some(id) if id == &self.target => { +                        state.focus(); +                    } +                    _ => { +                        state.unfocus(); +                    } +                } +            } +        } + +        fn container( +            &mut self, +            _id: Option<&Id>, +            operate_on_children: &dyn Fn(&mut dyn Operation<T>), +        ) { +            operate_on_children(self) +        } +    } + +    Focus { target } +} diff --git a/native/src/widget/state.rs b/native/src/widget/state.rs new file mode 100644 index 00000000..d1984a71 --- /dev/null +++ b/native/src/widget/state.rs @@ -0,0 +1,5 @@ +pub trait Focusable { +    fn is_focused(&self) -> bool; +    fn focus(&mut self); +    fn unfocus(&mut self); +} diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 4ef9e11b..1dbb8d6b 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -19,6 +19,7 @@ use crate::mouse::{self, click};  use crate::renderer;  use crate::text::{self, Text};  use crate::touch; +use crate::widget::state;  use crate::widget::tree::{self, Tree};  use crate::{      Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle, @@ -942,6 +943,20 @@ impl State {      }  } +impl state::Focusable for State { +    fn is_focused(&self) -> bool { +        State::is_focused(self) +    } + +    fn focus(&mut self) { +        State::focus(self) +    } + +    fn unfocus(&mut self) { +        State::unfocus(self) +    } +} +  mod platform {      use crate::keyboard;  | 
