From 80688689aa4b15bc23824df899974a9094a77b07 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 28 Jul 2022 02:46:51 +0200 Subject: Draft widget operations --- native/src/widget/action.rs | 78 +++++++++++++++++++++++++++++++++++++++++ native/src/widget/helpers.rs | 4 +-- native/src/widget/id.rs | 38 ++++++++++++++++++++ native/src/widget/operation.rs | 62 ++++++++++++++++++++++++++++++++ native/src/widget/state.rs | 5 +++ native/src/widget/text_input.rs | 15 ++++++++ 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 native/src/widget/action.rs create mode 100644 native/src/widget/id.rs create mode 100644 native/src/widget/operation.rs create mode 100644 native/src/widget/state.rs (limited to 'native/src/widget') 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(Box>); + +impl Action { + pub fn new(operation: impl Operation + 'static) -> Self { + Self(Box::new(operation)) + } + + pub fn map( + self, + f: impl Fn(T) -> A + 'static + MaybeSend + Sync, + ) -> Action + where + T: 'static, + A: 'static, + { + Action(Box::new(Map { + operation: self.0, + f: Box::new(f), + })) + } + + pub fn into_operation(self) -> Box> { + self.0 + } +} + +struct Map { + operation: Box>, + f: Box B>, +} + +impl Operation for Map +where + A: 'static, + B: 'static, +{ + fn container( + &mut self, + id: Option<&Id>, + operate_on_children: &dyn Fn(&mut dyn Operation), + ) { + struct MapRef<'a, A, B> { + operation: &'a mut dyn Operation, + f: &'a dyn Fn(A) -> B, + } + + impl<'a, A, B> Operation for MapRef<'a, A, B> { + fn container( + &mut self, + id: Option<&Id>, + operate_on_children: &dyn Fn(&mut dyn Operation), + ) { + 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( children: Vec>, -) -> 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>) -> 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 { + fn container( + &mut self, + id: Option<&Id>, + operate_on_children: &dyn Fn(&mut dyn Operation), + ); + + fn focusable( + &mut self, + _state: &mut dyn state::Focusable, + _id: Option<&Id>, + ) { + } + + fn finish(&self) -> Outcome { + Outcome::None + } +} + +pub enum Outcome { + None, + Some(T), + Chain(Box>), +} + +pub fn focus(target: Id) -> impl Operation { + struct Focus { + target: Id, + } + + impl Operation 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), + ) { + 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; -- cgit