diff options
Diffstat (limited to 'lazy/src')
| -rw-r--r-- | lazy/src/component.rs | 39 | ||||
| -rw-r--r-- | lazy/src/lazy.rs | 353 | ||||
| -rw-r--r-- | lazy/src/lib.rs | 15 | ||||
| -rw-r--r-- | lazy/src/responsive.rs | 22 | 
4 files changed, 402 insertions, 27 deletions
| diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 8987b993..3d7b8758 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -11,7 +11,7 @@ use iced_native::{  };  use ouroboros::self_referencing; -use std::cell::{Ref, RefCell}; +use std::cell::RefCell;  use std::marker::PhantomData;  /// A reusable, custom widget that uses The Elm Architecture. @@ -260,6 +260,14 @@ where              ) {                  self.operation.focusable(state, id);              } + +            fn text_input( +                &mut self, +                state: &mut dyn widget::operation::TextInput, +                id: Option<&widget::Id>, +            ) { +                self.operation.text_input(state, id); +            }          }          self.with_element(|element| { @@ -314,25 +322,25 @@ where      }      fn overlay<'b>( -        &'b self, +        &'b mut self,          tree: &'b mut Tree,          layout: Layout<'_>,          renderer: &Renderer,      ) -> Option<overlay::Element<'b, Message, Renderer>> {          let overlay = OverlayBuilder {              instance: self, -            instance_ref_builder: |instance| instance.state.borrow(),              tree,              types: PhantomData,              overlay_builder: |instance, tree| { -                instance -                    .as_ref() -                    .unwrap() -                    .borrow_element() -                    .as_ref() -                    .unwrap() -                    .as_widget() -                    .overlay(&mut tree.children[0], layout, renderer) +                instance.state.get_mut().as_mut().unwrap().with_element_mut( +                    move |element| { +                        element.as_mut().unwrap().as_widget_mut().overlay( +                            &mut tree.children[0], +                            layout, +                            renderer, +                        ) +                    }, +                )              },          }          .build(); @@ -354,15 +362,11 @@ where  #[self_referencing]  struct Overlay<'a, 'b, Message, Renderer, Event, S> { -    instance: &'a Instance<'b, Message, Renderer, Event, S>, +    instance: &'a mut Instance<'b, Message, Renderer, Event, S>,      tree: &'a mut Tree,      types: PhantomData<(Message, Event, S)>, -    #[borrows(instance)] -    #[covariant] -    instance_ref: Ref<'this, Option<State<'a, Message, Renderer, Event, S>>>, - -    #[borrows(instance_ref, mut tree)] +    #[borrows(mut instance, mut tree)]      #[covariant]      overlay: Option<overlay::Element<'this, Event, Renderer>>,  } @@ -506,7 +510,6 @@ where              self.overlay = Some(                  OverlayBuilder {                      instance: overlay.instance, -                    instance_ref_builder: |instance| instance.state.borrow(),                      tree: overlay.tree,                      types: PhantomData,                      overlay_builder: |_, _| None, diff --git a/lazy/src/lazy.rs b/lazy/src/lazy.rs new file mode 100644 index 00000000..2611dd10 --- /dev/null +++ b/lazy/src/lazy.rs @@ -0,0 +1,353 @@ +use iced_native::event; +use iced_native::layout::{self, Layout}; +use iced_native::mouse; +use iced_native::overlay; +use iced_native::renderer; +use iced_native::widget::tree::{self, Tree}; +use iced_native::widget::{self, Widget}; +use iced_native::Element; +use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell, Size}; + +use ouroboros::self_referencing; +use std::cell::{Ref, RefCell, RefMut}; +use std::hash::{Hash, Hasher as H}; +use std::marker::PhantomData; +use std::rc::Rc; + +#[allow(missing_debug_implementations)] +pub struct Lazy<'a, Message, Renderer, Dependency, View> { +    dependency: Dependency, +    view: Box<dyn Fn() -> View + 'a>, +    element: RefCell<Option<Rc<RefCell<Element<'static, Message, Renderer>>>>>, +} + +impl<'a, Message, Renderer, Dependency, View> +    Lazy<'a, Message, Renderer, Dependency, View> +where +    Dependency: Hash + 'a, +    View: Into<Element<'static, Message, Renderer>>, +{ +    pub fn new(dependency: Dependency, view: impl Fn() -> View + 'a) -> Self { +        Self { +            dependency, +            view: Box::new(view), +            element: RefCell::new(None), +        } +    } + +    fn with_element<T>( +        &self, +        f: impl FnOnce(Ref<Element<Message, Renderer>>) -> T, +    ) -> T { +        f(self.element.borrow().as_ref().unwrap().borrow()) +    } + +    fn with_element_mut<T>( +        &self, +        f: impl FnOnce(RefMut<Element<Message, Renderer>>) -> T, +    ) -> T { +        f(self.element.borrow().as_ref().unwrap().borrow_mut()) +    } +} + +struct Internal<Message, Renderer> { +    element: Rc<RefCell<Element<'static, Message, Renderer>>>, +    hash: u64, +} + +impl<'a, Message, Renderer, Dependency, View> Widget<Message, Renderer> +    for Lazy<'a, Message, Renderer, Dependency, View> +where +    View: Into<Element<'static, Message, Renderer>> + 'static, +    Dependency: Hash + 'a, +    Message: 'static, +    Renderer: iced_native::Renderer + 'static, +{ +    fn tag(&self) -> tree::Tag { +        struct Tag<T>(T); +        tree::Tag::of::<Tag<View>>() +    } + +    fn state(&self) -> tree::State { +        let mut hasher = Hasher::default(); +        self.dependency.hash(&mut hasher); +        let hash = hasher.finish(); + +        let element = Rc::new(RefCell::new((self.view)().into())); + +        (*self.element.borrow_mut()) = Some(element.clone()); + +        tree::State::new(Internal { element, hash }) +    } + +    fn children(&self) -> Vec<Tree> { +        vec![Tree::new( +            self.element.borrow().as_ref().unwrap().borrow().as_widget(), +        )] +    } + +    fn diff(&self, tree: &mut Tree) { +        let current = tree.state.downcast_mut::<Internal<Message, Renderer>>(); + +        let mut hasher = Hasher::default(); +        self.dependency.hash(&mut hasher); +        let new_hash = hasher.finish(); + +        if current.hash != new_hash { +            current.hash = new_hash; + +            let element = (self.view)().into(); +            current.element = Rc::new(RefCell::new(element)); + +            (*self.element.borrow_mut()) = Some(current.element.clone()); +            tree.diff_children(std::slice::from_ref( +                &self.element.borrow().as_ref().unwrap().borrow().as_widget(), +            )); +        } else { +            (*self.element.borrow_mut()) = Some(current.element.clone()); +        } +    } + +    fn width(&self) -> Length { +        self.with_element(|element| element.as_widget().width()) +    } + +    fn height(&self) -> Length { +        self.with_element(|element| element.as_widget().height()) +    } + +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        self.with_element(|element| { +            element.as_widget().layout(renderer, limits) +        }) +    } + +    fn operate( +        &self, +        tree: &mut Tree, +        layout: Layout<'_>, +        operation: &mut dyn widget::Operation<Message>, +    ) { +        self.with_element(|element| { +            element.as_widget().operate( +                &mut tree.children[0], +                layout, +                operation, +            ); +        }); +    } + +    fn on_event( +        &mut self, +        tree: &mut Tree, +        event: iced_native::Event, +        layout: Layout<'_>, +        cursor_position: Point, +        renderer: &Renderer, +        clipboard: &mut dyn Clipboard, +        shell: &mut Shell<'_, Message>, +    ) -> event::Status { +        self.with_element_mut(|mut element| { +            element.as_widget_mut().on_event( +                &mut tree.children[0], +                event, +                layout, +                cursor_position, +                renderer, +                clipboard, +                shell, +            ) +        }) +    } + +    fn mouse_interaction( +        &self, +        tree: &Tree, +        layout: Layout<'_>, +        cursor_position: Point, +        viewport: &Rectangle, +        renderer: &Renderer, +    ) -> mouse::Interaction { +        self.with_element(|element| { +            element.as_widget().mouse_interaction( +                &tree.children[0], +                layout, +                cursor_position, +                viewport, +                renderer, +            ) +        }) +    } + +    fn draw( +        &self, +        tree: &Tree, +        renderer: &mut Renderer, +        theme: &Renderer::Theme, +        style: &renderer::Style, +        layout: Layout<'_>, +        cursor_position: Point, +        viewport: &Rectangle, +    ) { +        self.with_element(|element| { +            element.as_widget().draw( +                &tree.children[0], +                renderer, +                theme, +                style, +                layout, +                cursor_position, +                viewport, +            ) +        }) +    } + +    fn overlay<'b>( +        &'b mut self, +        tree: &'b mut Tree, +        layout: Layout<'_>, +        renderer: &Renderer, +    ) -> Option<overlay::Element<'_, Message, Renderer>> { +        let overlay = OverlayBuilder { +            cached: self, +            tree: &mut tree.children[0], +            types: PhantomData, +            overlay_builder: |cached, tree| { +                Rc::get_mut(cached.element.get_mut().as_mut().unwrap()) +                    .unwrap() +                    .get_mut() +                    .as_widget_mut() +                    .overlay(tree, layout, renderer) +            }, +        } +        .build(); + +        let has_overlay = overlay.with_overlay(|overlay| { +            overlay.as_ref().map(overlay::Element::position) +        }); + +        has_overlay +            .map(|position| overlay::Element::new(position, Box::new(overlay))) +    } +} + +#[self_referencing] +struct Overlay<'a, 'b, Message, Renderer, Dependency, View> { +    cached: &'a mut Lazy<'b, Message, Renderer, Dependency, View>, +    tree: &'a mut Tree, +    types: PhantomData<(Message, Dependency, View)>, + +    #[borrows(mut cached, mut tree)] +    #[covariant] +    overlay: Option<overlay::Element<'this, Message, Renderer>>, +} + +impl<'a, 'b, Message, Renderer, Dependency, View> +    Overlay<'a, 'b, Message, Renderer, Dependency, View> +{ +    fn with_overlay_maybe<T>( +        &self, +        f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, +    ) -> Option<T> { +        self.borrow_overlay().as_ref().map(f) +    } + +    fn with_overlay_mut_maybe<T>( +        &mut self, +        f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, +    ) -> Option<T> { +        self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) +    } +} + +impl<'a, 'b, Message, Renderer, Dependency, View> +    overlay::Overlay<Message, Renderer> +    for Overlay<'a, 'b, Message, Renderer, Dependency, View> +where +    Renderer: iced_native::Renderer, +{ +    fn layout( +        &self, +        renderer: &Renderer, +        bounds: Size, +        position: Point, +    ) -> layout::Node { +        self.with_overlay_maybe(|overlay| { +            let vector = position - overlay.position(); + +            overlay.layout(renderer, bounds).translate(vector) +        }) +        .unwrap_or_default() +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        theme: &Renderer::Theme, +        style: &renderer::Style, +        layout: Layout<'_>, +        cursor_position: Point, +    ) { +        let _ = self.with_overlay_maybe(|overlay| { +            overlay.draw(renderer, theme, style, layout, cursor_position); +        }); +    } + +    fn mouse_interaction( +        &self, +        layout: Layout<'_>, +        cursor_position: Point, +        viewport: &Rectangle, +        renderer: &Renderer, +    ) -> mouse::Interaction { +        self.with_overlay_maybe(|overlay| { +            overlay.mouse_interaction( +                layout, +                cursor_position, +                viewport, +                renderer, +            ) +        }) +        .unwrap_or_default() +    } + +    fn on_event( +        &mut self, +        event: iced_native::Event, +        layout: Layout<'_>, +        cursor_position: Point, +        renderer: &Renderer, +        clipboard: &mut dyn Clipboard, +        shell: &mut Shell<'_, Message>, +    ) -> event::Status { +        self.with_overlay_mut_maybe(|overlay| { +            overlay.on_event( +                event, +                layout, +                cursor_position, +                renderer, +                clipboard, +                shell, +            ) +        }) +        .unwrap_or(iced_native::event::Status::Ignored) +    } +} + +impl<'a, Message, Renderer, Dependency, View> +    From<Lazy<'a, Message, Renderer, Dependency, View>> +    for Element<'a, Message, Renderer> +where +    View: Into<Element<'static, Message, Renderer>> + 'static, +    Renderer: iced_native::Renderer + 'static, +    Message: 'static, +    Dependency: Hash + 'a, +{ +    fn from(lazy: Lazy<'a, Message, Renderer, Dependency, View>) -> Self { +        Self::new(lazy) +    } +} diff --git a/lazy/src/lib.rs b/lazy/src/lib.rs index 3827746c..f49fe4b6 100644 --- a/lazy/src/lib.rs +++ b/lazy/src/lib.rs @@ -17,15 +17,30 @@      clippy::type_complexity  )]  #![cfg_attr(docsrs, feature(doc_cfg))] +mod lazy; +  pub mod component;  pub mod responsive;  pub use component::Component; +pub use lazy::Lazy;  pub use responsive::Responsive;  mod cache;  use iced_native::{Element, Size}; +use std::hash::Hash; + +pub fn lazy<'a, Message, Renderer, Dependency, View>( +    dependency: Dependency, +    view: impl Fn() -> View + 'a, +) -> Lazy<'a, Message, Renderer, Dependency, View> +where +    Dependency: Hash + 'a, +    View: Into<Element<'static, Message, Renderer>>, +{ +    Lazy::new(dependency, view) +}  /// Turns an implementor of [`Component`] into an [`Element`] that can be  /// embedded in any application. diff --git a/lazy/src/responsive.rs b/lazy/src/responsive.rs index 0b7ae6de..5e1b5dff 100644 --- a/lazy/src/responsive.rs +++ b/lazy/src/responsive.rs @@ -235,18 +235,20 @@ where      }      fn overlay<'b>( -        &'b self, +        &'b mut self,          tree: &'b mut Tree,          layout: Layout<'_>,          renderer: &Renderer,      ) -> Option<overlay::Element<'b, Message, Renderer>> { +        use std::ops::DerefMut; +          let state = tree.state.downcast_ref::<State>();          let overlay = OverlayBuilder {              content: self.content.borrow_mut(),              tree: state.tree.borrow_mut(),              types: PhantomData, -            overlay_builder: |content, tree| { +            overlay_builder: |content: &mut RefMut<Content<_, _>>, tree| {                  content.update(                      tree,                      renderer, @@ -254,16 +256,18 @@ where                      &self.view,                  ); +                let Content { +                    element, layout, .. +                } = content.deref_mut(); +                  let content_layout = Layout::with_offset( -                    layout.position() - Point::ORIGIN, -                    &content.layout, +                    layout.bounds().position() - Point::ORIGIN, +                    layout,                  ); -                content.element.as_widget().overlay( -                    tree, -                    content_layout, -                    renderer, -                ) +                element +                    .as_widget_mut() +                    .overlay(tree, content_layout, renderer)              },          }          .build(); | 
