From 3a0d34c0240f4421737a6a08761f99d6f8140d02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 4 Mar 2023 05:37:11 +0100 Subject: Create `iced_widget` subcrate and re-organize the whole codebase --- widget/src/lazy.rs | 409 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 widget/src/lazy.rs (limited to 'widget/src/lazy.rs') diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs new file mode 100644 index 00000000..b08ed8cb --- /dev/null +++ b/widget/src/lazy.rs @@ -0,0 +1,409 @@ +#![allow(clippy::await_holding_refcell_ref, clippy::type_complexity)] +pub(crate) mod helpers; + +pub mod component; +pub mod responsive; + +pub use component::Component; +pub use responsive::Responsive; + +mod cache; + +use crate::core::event::{self, Event}; +use crate::core::layout::{self, Layout}; +use crate::core::mouse; +use crate::core::overlay; +use crate::core::renderer; +use crate::core::widget::tree::{self, Tree}; +use crate::core::widget::{self, Widget}; +use crate::core::Element; +use crate::core::{ + self, Clipboard, Hasher, Length, Point, Rectangle, Shell, Size, +}; + +use ouroboros::self_referencing; +use std::cell::RefCell; +use std::hash::{Hash, Hasher as H}; +use std::rc::Rc; + +#[allow(missing_debug_implementations)] +pub struct Lazy<'a, Message, Renderer, Dependency, View> { + dependency: Dependency, + view: Box View + 'a>, + element: RefCell< + Option>>>>, + >, +} + +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(&Dependency) -> View + 'a, + ) -> Self { + Self { + dependency, + view: Box::new(view), + element: RefCell::new(None), + } + } + + fn with_element( + &self, + f: impl FnOnce(&Element<'_, Message, Renderer>) -> T, + ) -> T { + f(self + .element + .borrow() + .as_ref() + .unwrap() + .borrow() + .as_ref() + .unwrap()) + } + + fn with_element_mut( + &self, + f: impl FnOnce(&mut Element<'_, Message, Renderer>) -> T, + ) -> T { + f(self + .element + .borrow() + .as_ref() + .unwrap() + .borrow_mut() + .as_mut() + .unwrap()) + } +} + +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: core::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(Some((self.view)(&self.dependency).into()))); + + (*self.element.borrow_mut()) = Some(element.clone()); + + tree::State::new(Internal { element, hash }) + } + + fn children(&self) -> Vec { + self.with_element(|element| vec![Tree::new(element.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)(&self.dependency).into(); + current.element = Rc::new(RefCell::new(Some(element))); + + (*self.element.borrow_mut()) = Some(current.element.clone()); + self.with_element(|element| { + tree.diff_children(std::slice::from_ref(&element.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<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + self.with_element(|element| { + element.as_widget().operate( + &mut tree.children[0], + layout, + renderer, + operation, + ); + }); + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + self.with_element_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> { + let overlay = Overlay(Some( + InnerBuilder { + cell: self.element.borrow().as_ref().unwrap().clone(), + element: self + .element + .borrow() + .as_ref() + .unwrap() + .borrow_mut() + .take() + .unwrap(), + tree: &mut tree.children[0], + overlay_builder: |element, tree| { + element.as_widget_mut().overlay(tree, layout, renderer) + }, + } + .build(), + )); + + let has_overlay = overlay + .with_overlay_maybe(|overlay| overlay::Element::position(overlay)); + + has_overlay + .map(|position| overlay::Element::new(position, Box::new(overlay))) + } +} + +#[self_referencing] +struct Inner<'a, Message, Renderer> +where + Message: 'a, + Renderer: 'a, +{ + cell: Rc>>>, + element: Element<'static, Message, Renderer>, + tree: &'a mut Tree, + + #[borrows(mut element, mut tree)] + #[covariant] + overlay: Option>, +} + +struct Overlay<'a, Message, Renderer>(Option>); + +impl<'a, Message, Renderer> Drop for Overlay<'a, Message, Renderer> { + fn drop(&mut self) { + let heads = self.0.take().unwrap().into_heads(); + (*heads.cell.borrow_mut()) = Some(heads.element); + } +} + +impl<'a, Message, Renderer> Overlay<'a, Message, Renderer> { + fn with_overlay_maybe( + &self, + f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, + ) -> Option { + self.0.as_ref().unwrap().borrow_overlay().as_ref().map(f) + } + + fn with_overlay_mut_maybe( + &mut self, + f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, + ) -> Option { + self.0 + .as_mut() + .unwrap() + .with_overlay_mut(|overlay| overlay.as_mut().map(f)) + } +} + +impl<'a, Message, Renderer> overlay::Overlay + for Overlay<'a, Message, Renderer> +where + Renderer: core::Renderer, +{ + fn layout( + &self, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node { + self.with_overlay_maybe(|overlay| { + let translation = position - overlay.position(); + + overlay.layout(renderer, bounds, translation) + }) + .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: 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(event::Status::Ignored) + } + + fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + self.with_overlay_maybe(|overlay| { + overlay.is_over(layout, cursor_position) + }) + .unwrap_or_default() + } +} + +impl<'a, Message, Renderer, Dependency, View> + From> + for Element<'a, Message, Renderer> +where + View: Into> + 'static, + Renderer: core::Renderer + 'static, + Message: 'static, + Dependency: Hash + 'a, +{ + fn from(lazy: Lazy<'a, Message, Renderer, Dependency, View>) -> Self { + Self::new(lazy) + } +} -- cgit