From 1cdc1fcd0669bfea096237c07b32742c1a3f2158 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 3 Nov 2022 02:46:31 +0100 Subject: Rename `iced_lazy::Cached` to `Lazy` :tada: --- examples/cached/src/main.rs | 59 ++++---- lazy/src/cached.rs | 362 -------------------------------------------- lazy/src/lazy.rs | 362 ++++++++++++++++++++++++++++++++++++++++++++ lazy/src/lib.rs | 17 ++- 4 files changed, 405 insertions(+), 395 deletions(-) delete mode 100644 lazy/src/cached.rs create mode 100644 lazy/src/lazy.rs diff --git a/examples/cached/src/main.rs b/examples/cached/src/main.rs index 39364dc9..7c8b06f0 100644 --- a/examples/cached/src/main.rs +++ b/examples/cached/src/main.rs @@ -3,7 +3,7 @@ use iced::widget::{ button, column, horizontal_space, row, scrollable, text, text_input, }; use iced::{Element, Length, Sandbox, Settings}; -use iced_lazy::Cached; +use iced_lazy::lazy; use std::collections::HashSet; @@ -71,39 +71,36 @@ impl Sandbox for App { } fn view(&self) -> Element { - let options = - Cached::new((&self.sort_order, self.options.len()), || { - let mut options: Vec<_> = self.options.iter().collect(); + let options = lazy((&self.sort_order, self.options.len()), || { + let mut options: Vec<_> = self.options.iter().collect(); - options.sort_by(|a, b| match self.sort_order { - SortOrder::Ascending => { - a.to_lowercase().cmp(&b.to_lowercase()) - } - SortOrder::Descending => { - b.to_lowercase().cmp(&a.to_lowercase()) - } - }); - - column( - options - .into_iter() - .map(|option| { - row![ - text(option), - horizontal_space(Length::Fill), - button("Delete") - .on_press(Message::DeleteOption( - option.to_string(), - ),) - .style(theme::Button::Destructive) - ] - .into() - }) - .collect(), - ) - .spacing(10) + options.sort_by(|a, b| match self.sort_order { + SortOrder::Ascending => a.to_lowercase().cmp(&b.to_lowercase()), + SortOrder::Descending => { + b.to_lowercase().cmp(&a.to_lowercase()) + } }); + column( + options + .into_iter() + .map(|option| { + row![ + text(option), + horizontal_space(Length::Fill), + button("Delete") + .on_press(Message::DeleteOption( + option.to_string(), + ),) + .style(theme::Button::Destructive) + ] + .into() + }) + .collect(), + ) + .spacing(10) + }); + column![ scrollable(options).height(Length::Fill), row![ diff --git a/lazy/src/cached.rs b/lazy/src/cached.rs deleted file mode 100644 index 931184b5..00000000 --- a/lazy/src/cached.rs +++ /dev/null @@ -1,362 +0,0 @@ -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 Cached<'a, Message, Renderer, Dependency, View> { - dependency: Dependency, - view: Box View + 'a>, - element: RefCell>>>>, -} - -impl<'a, Message, Renderer, Dependency, View> - Cached<'a, Message, Renderer, Dependency, View> -where - Dependency: Hash + 'a, - View: Into>, -{ - pub fn new(dependency: Dependency, view: impl Fn() -> View + 'a) -> Self { - Self { - dependency, - view: Box::new(view), - element: RefCell::new(None), - } - } - - fn with_element( - &self, - f: impl FnOnce(Ref>) -> T, - ) -> T { - f(self.element.borrow().as_ref().unwrap().borrow()) - } - - fn with_element_mut( - &self, - f: impl FnOnce(RefMut>) -> T, - ) -> T { - f(self.element.borrow().as_ref().unwrap().borrow_mut()) - } -} - -struct Internal { - element: Rc>>, - hash: u64, -} - -impl<'a, Message, Renderer, Dependency, View> Widget - for Cached<'a, Message, Renderer, Dependency, View> -where - View: Into> + 'static, - Dependency: Hash + 'a, - Message: 'static, - Renderer: iced_native::Renderer + 'static, -{ - fn tag(&self) -> tree::Tag { - struct Tag(T); - tree::Tag::of::>() - } - - 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 { - vec![Tree::new( - self.element.borrow().as_ref().unwrap().borrow().as_widget(), - )] - } - - fn diff(&self, tree: &mut Tree) { - let current = tree.state.downcast_mut::>(); - - 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, - ) { - 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 self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - let overlay = OverlayBuilder { - cached: self, - tree: &mut tree.children[0], - types: PhantomData, - element_ref_builder: |cached| cached.element.borrow(), - element_builder: |element_ref| { - element_ref.as_ref().unwrap().borrow() - }, - overlay_builder: |element, tree| { - element.as_widget().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 Cached<'b, Message, Renderer, Dependency, View>, - tree: &'a mut Tree, - types: PhantomData<(Message, Dependency, View)>, - - #[borrows(cached)] - #[covariant] - element_ref: - Ref<'this, Option>>>>, - - #[borrows(element_ref)] - #[covariant] - element: Ref<'this, Element<'static, Message, Renderer>>, - - #[borrows(element, mut tree)] - #[covariant] - overlay: Option>, -} - -impl<'a, 'b, Message, Renderer, Dependency, View> - Overlay<'a, 'b, Message, Renderer, Dependency, View> -{ - fn with_overlay_maybe( - &self, - f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, - ) -> Option { - self.borrow_overlay().as_ref().map(f) - } - - fn with_overlay_mut_maybe( - &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, - ) -> Option { - self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) - } -} - -impl<'a, 'b, Message, Renderer, Dependency, View> - overlay::Overlay - 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> - for Element<'a, Message, Renderer> -where - View: Into> + 'static, - Renderer: iced_native::Renderer + 'static, - Message: 'static, - Dependency: Hash + 'a, -{ - fn from(cached: Cached<'a, Message, Renderer, Dependency, View>) -> Self { - Self::new(cached) - } -} diff --git a/lazy/src/lazy.rs b/lazy/src/lazy.rs new file mode 100644 index 00000000..d61cc77e --- /dev/null +++ b/lazy/src/lazy.rs @@ -0,0 +1,362 @@ +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 View + 'a>, + element: RefCell>>>>, +} + +impl<'a, Message, Renderer, Dependency, View> + Lazy<'a, Message, Renderer, Dependency, View> +where + Dependency: Hash + 'a, + View: Into>, +{ + pub fn new(dependency: Dependency, view: impl Fn() -> View + 'a) -> Self { + Self { + dependency, + view: Box::new(view), + element: RefCell::new(None), + } + } + + fn with_element( + &self, + f: impl FnOnce(Ref>) -> T, + ) -> T { + f(self.element.borrow().as_ref().unwrap().borrow()) + } + + fn with_element_mut( + &self, + f: impl FnOnce(RefMut>) -> T, + ) -> T { + f(self.element.borrow().as_ref().unwrap().borrow_mut()) + } +} + +struct Internal { + element: Rc>>, + hash: u64, +} + +impl<'a, Message, Renderer, Dependency, View> Widget + for Lazy<'a, Message, Renderer, Dependency, View> +where + View: Into> + 'static, + Dependency: Hash + 'a, + Message: 'static, + Renderer: iced_native::Renderer + 'static, +{ + fn tag(&self) -> tree::Tag { + struct Tag(T); + tree::Tag::of::>() + } + + 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 { + vec![Tree::new( + self.element.borrow().as_ref().unwrap().borrow().as_widget(), + )] + } + + fn diff(&self, tree: &mut Tree) { + let current = tree.state.downcast_mut::>(); + + 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, + ) { + 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 self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + let overlay = OverlayBuilder { + cached: self, + tree: &mut tree.children[0], + types: PhantomData, + element_ref_builder: |cached| cached.element.borrow(), + element_builder: |element_ref| { + element_ref.as_ref().unwrap().borrow() + }, + overlay_builder: |element, tree| { + element.as_widget().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 Lazy<'b, Message, Renderer, Dependency, View>, + tree: &'a mut Tree, + types: PhantomData<(Message, Dependency, View)>, + + #[borrows(cached)] + #[covariant] + element_ref: + Ref<'this, Option>>>>, + + #[borrows(element_ref)] + #[covariant] + element: Ref<'this, Element<'static, Message, Renderer>>, + + #[borrows(element, mut tree)] + #[covariant] + overlay: Option>, +} + +impl<'a, 'b, Message, Renderer, Dependency, View> + Overlay<'a, 'b, Message, Renderer, Dependency, View> +{ + fn with_overlay_maybe( + &self, + f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, + ) -> Option { + self.borrow_overlay().as_ref().map(f) + } + + fn with_overlay_mut_maybe( + &mut self, + f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, + ) -> Option { + self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) + } +} + +impl<'a, 'b, Message, Renderer, Dependency, View> + overlay::Overlay + 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> + for Element<'a, Message, Renderer> +where + View: Into> + '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 c01b439b..f49fe4b6 100644 --- a/lazy/src/lib.rs +++ b/lazy/src/lib.rs @@ -17,17 +17,30 @@ clippy::type_complexity )] #![cfg_attr(docsrs, feature(doc_cfg))] -pub mod cached; +mod lazy; + pub mod component; pub mod responsive; -pub use cached::Cached; 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>, +{ + Lazy::new(dependency, view) +} /// Turns an implementor of [`Component`] into an [`Element`] that can be /// embedded in any application. -- cgit