From bffa7203dfd333b699bc29a22c74fb602eea4ea1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Nov 2021 18:15:01 +0700 Subject: Create `iced_lazy` and draft `Component` trait --- lazy/src/component.rs | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++ lazy/src/lib.rs | 3 + 2 files changed, 184 insertions(+) create mode 100644 lazy/src/component.rs create mode 100644 lazy/src/lib.rs (limited to 'lazy/src') 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, +) -> 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 { + type Event; + + fn update(&mut self, event: Self::Event) -> Option; + + fn view(&mut self) -> Element; +} + +struct Instance<'a, Message, Renderer, Event> { + state: Option>, +} + +#[self_referencing] +struct State<'a, Message, Renderer, Event> { + component: &'a mut dyn Component, + + #[borrows(mut component)] + #[covariant] + cache: Cache<'this, Message, Renderer, Event>, +} + +struct Cache<'a, Message, Renderer, Event> { + element: Element<'a, Event, Renderer>, + message: PhantomData, +} + +impl<'a, Message, Renderer, Event> Widget + 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, + ) -> 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> { + // 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; -- cgit From 010b62b9ee20b03053ab538a5910795bc0618378 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 Nov 2021 15:29:58 +0700 Subject: Draft `component` example :tada: --- lazy/src/component.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'lazy/src') diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 3296533e..04832d9d 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -11,10 +11,12 @@ use ouroboros::self_referencing; use std::marker::PhantomData; pub fn view<'a, Event, Message, Renderer>( - component: &'a mut dyn Component, + component: Box + 'a>, ) -> Element<'a, Message, Renderer> where - Renderer: iced_native::Renderer, + Message: 'a, + Event: 'a, + Renderer: iced_native::Renderer + 'a, { Element::new(Instance { state: Some( @@ -43,8 +45,8 @@ struct Instance<'a, Message, Renderer, Event> { } #[self_referencing] -struct State<'a, Message, Renderer, Event> { - component: &'a mut dyn Component, +struct State<'a, Message: 'a, Renderer: 'a, Event: 'a> { + component: Box + 'a>, #[borrows(mut component)] #[covariant] @@ -106,7 +108,8 @@ where }); if !local_messages.is_empty() { - let component = self.state.take().unwrap().into_heads().component; + let mut component = + self.state.take().unwrap().into_heads().component; messages.extend( local_messages -- cgit From f7792d89d64c39cdde9da030bec80fb6f461a0e3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 Nov 2021 15:32:58 +0700 Subject: Hide `Box` allocation in `component::view` ... we may be able to avoid it with generics in the future. --- lazy/src/component.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lazy/src') diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 04832d9d..6d57a2a9 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -10,18 +10,18 @@ use iced_native::{ use ouroboros::self_referencing; use std::marker::PhantomData; -pub fn view<'a, Event, Message, Renderer>( - component: Box + 'a>, +pub fn view<'a, C, Message, Renderer>( + component: C, ) -> Element<'a, Message, Renderer> where + C: Component + 'a, Message: 'a, - Event: 'a, Renderer: iced_native::Renderer + 'a, { Element::new(Instance { state: Some( StateBuilder { - component, + component: Box::new(component), cache_builder: |state| Cache { element: state.view(), message: PhantomData, -- cgit From bbd9355450bc2df3a2c0e37cc900ba00b26255af Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 29 Nov 2021 16:22:01 +0700 Subject: Introduce `Shell` type in `iced_native` Widgets now can invalidate the current layout of the application on demand. --- lazy/src/component.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'lazy/src') diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 6d57a2a9..aa5b847e 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -4,7 +4,7 @@ use iced_native::mouse; use iced_native::overlay; use iced_native::renderer; use iced_native::{ - Clipboard, Element, Hasher, Length, Point, Rectangle, Widget, + Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Widget, }; use ouroboros::self_referencing; @@ -91,9 +91,10 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, - messages: &mut Vec, + 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| { @@ -103,7 +104,7 @@ where cursor_position, renderer, clipboard, - &mut local_messages, + &mut local_shell, ) }); @@ -111,11 +112,12 @@ where let mut component = self.state.take().unwrap().into_heads().component; - messages.extend( - local_messages - .into_iter() - .filter_map(|message| component.update(message)), - ); + for message in local_messages + .into_iter() + .filter_map(|message| component.update(message)) + { + shell.publish(message); + } self.state = Some( StateBuilder { @@ -128,7 +130,7 @@ where .build(), ); - // TODO: Invalidate layout (!) + shell.invalidate_layout(); } event_status -- cgit