diff options
author | 2021-11-07 18:15:01 +0700 | |
---|---|---|
committer | 2021-11-29 15:04:12 +0700 | |
commit | bffa7203dfd333b699bc29a22c74fb602eea4ea1 (patch) | |
tree | 6e9315aa6bcb52cf0b95a0698c88b7aecde79186 /lazy | |
parent | d9f970ffd5af6dafb5e696ad317d9ea7b997eb4b (diff) | |
download | iced-bffa7203dfd333b699bc29a22c74fb602eea4ea1.tar.gz iced-bffa7203dfd333b699bc29a22c74fb602eea4ea1.tar.bz2 iced-bffa7203dfd333b699bc29a22c74fb602eea4ea1.zip |
Create `iced_lazy` and draft `Component` trait
Diffstat (limited to 'lazy')
-rw-r--r-- | lazy/Cargo.toml | 11 | ||||
-rw-r--r-- | lazy/src/component.rs | 181 | ||||
-rw-r--r-- | lazy/src/lib.rs | 3 |
3 files changed, 195 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..3296533e --- /dev/null +++ b/lazy/src/component.rs @@ -0,0 +1,181 @@ +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, Widget, +}; + +use ouroboros::self_referencing; +use std::marker::PhantomData; + +pub fn view<'a, Event, Message, Renderer>( + component: &'a mut dyn Component<Message, Renderer, Event = Event>, +) -> Element<'a, Message, Renderer> +where + Renderer: iced_native::Renderer, +{ + Element::new(Instance { + state: Some( + StateBuilder { + 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, Renderer, Event> { + component: &'a mut dyn Component<Message, Renderer, Event = Event>, + + #[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, + messages: &mut Vec<Message>, + ) -> event::Status { + let mut local_messages = Vec::new(); + + let event_status = + self.state.as_mut().unwrap().with_cache_mut(|cache| { + cache.element.on_event( + event, + layout, + cursor_position, + renderer, + clipboard, + &mut local_messages, + ) + }); + + if !local_messages.is_empty() { + let component = self.state.take().unwrap().into_heads().component; + + messages.extend( + local_messages + .into_iter() + .filter_map(|message| component.update(message)), + ); + + self.state = Some( + StateBuilder { + component, + cache_builder: |state| Cache { + element: state.view(), + message: PhantomData, + }, + } + .build(), + ); + + // TODO: 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; |