diff options
Diffstat (limited to 'lazy')
| -rw-r--r-- | lazy/Cargo.toml | 11 | ||||
| -rw-r--r-- | lazy/src/component.rs | 186 | ||||
| -rw-r--r-- | lazy/src/lib.rs | 3 | 
3 files changed, 200 insertions, 0 deletions
| diff --git a/lazy/Cargo.toml b/lazy/Cargo.toml new file mode 100644 index 00000000..b840de50 --- /dev/null +++ b/lazy/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "iced_lazy" +version = "0.1.0" +edition = "2021" + +[dependencies] +ouroboros = "0.13" + +[dependencies.iced_native] +version = "0.4" +path = "../native" diff --git a/lazy/src/component.rs b/lazy/src/component.rs new file mode 100644 index 00000000..aa5b847e --- /dev/null +++ b/lazy/src/component.rs @@ -0,0 +1,186 @@ +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::{ +    Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Widget, +}; + +use ouroboros::self_referencing; +use std::marker::PhantomData; + +pub fn view<'a, C, Message, Renderer>( +    component: C, +) -> Element<'a, Message, Renderer> +where +    C: Component<Message, Renderer> + 'a, +    Message: 'a, +    Renderer: iced_native::Renderer + 'a, +{ +    Element::new(Instance { +        state: Some( +            StateBuilder { +                component: Box::new(component), +                cache_builder: |state| Cache { +                    element: state.view(), +                    message: PhantomData, +                }, +            } +            .build(), +        ), +    }) +} + +pub trait Component<Message, Renderer> { +    type Event; + +    fn update(&mut self, event: Self::Event) -> Option<Message>; + +    fn view(&mut self) -> Element<Self::Event, Renderer>; +} + +struct Instance<'a, Message, Renderer, Event> { +    state: Option<State<'a, Message, Renderer, Event>>, +} + +#[self_referencing] +struct State<'a, Message: 'a, Renderer: 'a, Event: 'a> { +    component: Box<dyn Component<Message, Renderer, Event = Event> + 'a>, + +    #[borrows(mut component)] +    #[covariant] +    cache: Cache<'this, Message, Renderer, Event>, +} + +struct Cache<'a, Message, Renderer, Event> { +    element: Element<'a, Event, Renderer>, +    message: PhantomData<Message>, +} + +impl<'a, Message, Renderer, Event> Widget<Message, Renderer> +    for Instance<'a, Message, Renderer, Event> +where +    Renderer: iced_native::Renderer, +{ +    fn width(&self) -> Length { +        self.state.as_ref().unwrap().borrow_cache().element.width() +    } + +    fn height(&self) -> Length { +        self.state.as_ref().unwrap().borrow_cache().element.width() +    } + +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        self.state +            .as_ref() +            .unwrap() +            .borrow_cache() +            .element +            .layout(renderer, limits) +    } + +    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 { +        let mut local_messages = Vec::new(); +        let mut local_shell = Shell::new(&mut local_messages); + +        let event_status = +            self.state.as_mut().unwrap().with_cache_mut(|cache| { +                cache.element.on_event( +                    event, +                    layout, +                    cursor_position, +                    renderer, +                    clipboard, +                    &mut local_shell, +                ) +            }); + +        if !local_messages.is_empty() { +            let mut component = +                self.state.take().unwrap().into_heads().component; + +            for message in local_messages +                .into_iter() +                .filter_map(|message| component.update(message)) +            { +                shell.publish(message); +            } + +            self.state = Some( +                StateBuilder { +                    component, +                    cache_builder: |state| Cache { +                        element: state.view(), +                        message: PhantomData, +                    }, +                } +                .build(), +            ); + +            shell.invalidate_layout(); +        } + +        event_status +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        style: &renderer::Style, +        layout: Layout<'_>, +        cursor_position: Point, +        viewport: &Rectangle, +    ) { +        self.state.as_ref().unwrap().borrow_cache().element.draw( +            renderer, +            style, +            layout, +            cursor_position, +            viewport, +        ) +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        self.state +            .as_ref() +            .unwrap() +            .borrow_cache() +            .element +            .hash_layout(state) +    } + +    fn mouse_interaction( +        &self, +        layout: Layout<'_>, +        cursor_position: Point, +        viewport: &Rectangle, +    ) -> mouse::Interaction { +        self.state +            .as_ref() +            .unwrap() +            .borrow_cache() +            .element +            .mouse_interaction(layout, cursor_position, viewport) +    } + +    fn overlay( +        &mut self, +        _layout: Layout<'_>, +    ) -> Option<overlay::Element<'_, Message, Renderer>> { +        // TODO: Rethink overlay composability +        None +    } +} diff --git a/lazy/src/lib.rs b/lazy/src/lib.rs new file mode 100644 index 00000000..42e5f587 --- /dev/null +++ b/lazy/src/lib.rs @@ -0,0 +1,3 @@ +pub mod component; + +pub use component::Component; | 
