diff options
Diffstat (limited to '')
-rw-r--r-- | widget/src/lazy.rs (renamed from lazy/src/lazy.rs) | 134 | ||||
-rw-r--r-- | widget/src/lazy/cache.rs (renamed from lazy/src/cache.rs) | 4 | ||||
-rw-r--r-- | widget/src/lazy/component.rs (renamed from lazy/src/component.rs) | 356 | ||||
-rw-r--r-- | widget/src/lazy/helpers.rs (renamed from lazy/src/lib.rs) | 45 | ||||
-rw-r--r-- | widget/src/lazy/responsive.rs (renamed from lazy/src/responsive.rs) | 181 |
5 files changed, 408 insertions, 312 deletions
diff --git a/lazy/src/lazy.rs b/widget/src/lazy.rs index 5e909a49..da287f06 100644 --- a/lazy/src/lazy.rs +++ b/widget/src/lazy.rs @@ -1,18 +1,33 @@ -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}; +#![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 crate::runtime::overlay::Nested; use ouroboros::self_referencing; use std::cell::RefCell; use std::hash::{Hash, Hasher as H}; use std::rc::Rc; +/// A widget that only rebuilds its contents when necessary. #[allow(missing_debug_implementations)] pub struct Lazy<'a, Message, Renderer, Dependency, View> { dependency: Dependency, @@ -28,6 +43,8 @@ where Dependency: Hash + 'a, View: Into<Element<'static, Message, Renderer>>, { + /// Creates a new [`Lazy`] widget with the given data `Dependency` and a + /// closure that can turn this data into a widget tree. pub fn new( dependency: Dependency, view: impl Fn(&Dependency) -> View + 'a, @@ -41,7 +58,7 @@ where fn with_element<T>( &self, - f: impl FnOnce(&Element<Message, Renderer>) -> T, + f: impl FnOnce(&Element<'_, Message, Renderer>) -> T, ) -> T { f(self .element @@ -55,7 +72,7 @@ where fn with_element_mut<T>( &self, - f: impl FnOnce(&mut Element<Message, Renderer>) -> T, + f: impl FnOnce(&mut Element<'_, Message, Renderer>) -> T, ) -> T { f(self .element @@ -79,7 +96,7 @@ where View: Into<Element<'static, Message, Renderer>> + 'static, Dependency: Hash + 'a, Message: 'static, - Renderer: iced_native::Renderer + 'static, + Renderer: core::Renderer + 'static, { fn tag(&self) -> tree::Tag { struct Tag<T>(T); @@ -163,9 +180,9 @@ where fn on_event( &mut self, tree: &mut Tree, - event: iced_native::Event, + event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -175,7 +192,7 @@ where &mut tree.children[0], event, layout, - cursor_position, + cursor, renderer, clipboard, shell, @@ -187,7 +204,7 @@ where &self, tree: &Tree, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { @@ -195,7 +212,7 @@ where element.as_widget().mouse_interaction( &tree.children[0], layout, - cursor_position, + cursor, viewport, renderer, ) @@ -209,7 +226,7 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ) { self.with_element(|element| { @@ -219,7 +236,7 @@ where theme, style, layout, - cursor_position, + cursor, viewport, ) }) @@ -244,14 +261,17 @@ where .unwrap(), tree: &mut tree.children[0], overlay_builder: |element, tree| { - element.as_widget_mut().overlay(tree, layout, renderer) + element + .as_widget_mut() + .overlay(tree, layout, renderer) + .map(|overlay| RefCell::new(Nested::new(overlay))) }, } .build(), )); - let has_overlay = overlay - .with_overlay_maybe(|overlay| overlay::Element::position(overlay)); + let has_overlay = + overlay.with_overlay_maybe(|overlay| overlay.position()); has_overlay .map(|position| overlay::Element::new(position, Box::new(overlay))) @@ -259,18 +279,14 @@ where } #[self_referencing] -struct Inner<'a, Message, Renderer> -where - Message: 'a, - Renderer: 'a, -{ +struct Inner<'a, Message: 'a, Renderer: 'a> { cell: Rc<RefCell<Option<Element<'static, Message, Renderer>>>>, element: Element<'static, Message, Renderer>, tree: &'a mut Tree, #[borrows(mut element, mut tree)] - #[covariant] - overlay: Option<overlay::Element<'this, Message, Renderer>>, + #[not_covariant] + overlay: Option<RefCell<Nested<'this, Message, Renderer>>>, } struct Overlay<'a, Message, Renderer>(Option<Inner<'a, Message, Renderer>>); @@ -285,26 +301,27 @@ impl<'a, Message, Renderer> Drop for Overlay<'a, Message, Renderer> { impl<'a, Message, Renderer> Overlay<'a, Message, Renderer> { fn with_overlay_maybe<T>( &self, - f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option<T> { - self.0.as_ref().unwrap().borrow_overlay().as_ref().map(f) + self.0.as_ref().unwrap().with_overlay(|overlay| { + overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) + }) } fn with_overlay_mut_maybe<T>( &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option<T> { - self.0 - .as_mut() - .unwrap() - .with_overlay_mut(|overlay| overlay.as_mut().map(f)) + self.0.as_mut().unwrap().with_overlay_mut(|overlay| { + overlay.as_mut().map(|nested| (f)(nested.get_mut())) + }) } } impl<'a, Message, Renderer> overlay::Overlay<Message, Renderer> for Overlay<'a, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: core::Renderer, { fn layout( &self, @@ -313,9 +330,7 @@ where position: Point, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - let translation = position - overlay.position(); - - overlay.layout(renderer, bounds, translation) + overlay.layout(renderer, bounds, position) }) .unwrap_or_default() } @@ -326,56 +341,49 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { let _ = self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, theme, style, layout, cursor_position); + overlay.draw(renderer, theme, style, layout, cursor); }); } fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.with_overlay_maybe(|overlay| { - overlay.mouse_interaction( - layout, - cursor_position, - viewport, - renderer, - ) + overlay.mouse_interaction(layout, cursor, viewport, renderer) }) .unwrap_or_default() } fn on_event( &mut self, - event: iced_native::Event, + event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, 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, - ) + overlay.on_event(event, layout, cursor, renderer, clipboard, shell) }) - .unwrap_or(iced_native::event::Status::Ignored) + .unwrap_or(event::Status::Ignored) } - fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + fn is_over( + &self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { self.with_overlay_maybe(|overlay| { - overlay.is_over(layout, cursor_position) + overlay.is_over(layout, renderer, cursor_position) }) .unwrap_or_default() } @@ -386,7 +394,7 @@ impl<'a, Message, Renderer, Dependency, View> for Element<'a, Message, Renderer> where View: Into<Element<'static, Message, Renderer>> + 'static, - Renderer: iced_native::Renderer + 'static, + Renderer: core::Renderer + 'static, Message: 'static, Dependency: Hash + 'a, { diff --git a/lazy/src/cache.rs b/widget/src/lazy/cache.rs index 5b4a39f6..e7b87614 100644 --- a/lazy/src/cache.rs +++ b/widget/src/lazy/cache.rs @@ -1,5 +1,5 @@ -use iced_native::overlay; -use iced_native::Element; +use crate::core::overlay; +use crate::core::Element; use ouroboros::self_referencing; diff --git a/lazy/src/component.rs b/widget/src/lazy/component.rs index b23da9f7..c7814966 100644 --- a/lazy/src/component.rs +++ b/widget/src/lazy/component.rs @@ -1,18 +1,20 @@ //! Build and reuse custom widgets using The Elm Architecture. -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; -use iced_native::widget::tree::{self, Tree}; -use iced_native::{ - Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, +use crate::core::event; +use crate::core::layout::{self, Layout}; +use crate::core::mouse; +use crate::core::overlay; +use crate::core::renderer; +use crate::core::widget; +use crate::core::widget::tree::{self, Tree}; +use crate::core::{ + self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; +use crate::runtime::overlay::Nested; use ouroboros::self_referencing; use std::cell::RefCell; use std::marker::PhantomData; +use std::rc::Rc; /// A reusable, custom widget that uses The Elm Architecture. /// @@ -58,6 +60,8 @@ pub trait Component<Message, Renderer> { } } +struct Tag<T>(T); + /// Turns an implementor of [`Component`] into an [`Element`] that can be /// embedded in any application. pub fn view<'a, C, Message, Renderer>( @@ -67,7 +71,7 @@ where C: Component<Message, Renderer> + 'a, C::State: 'static, Message: 'a, - Renderer: iced_native::Renderer + 'a, + Renderer: core::Renderer + 'a, { Element::new(Instance { state: RefCell::new(Some( @@ -79,11 +83,13 @@ where } .build(), )), + tree: RefCell::new(Rc::new(RefCell::new(None))), }) } struct Instance<'a, Message, Renderer, Event, S> { state: RefCell<Option<State<'a, Message, Renderer, Event, S>>>, + tree: RefCell<Rc<RefCell<Option<Tree>>>>, } #[self_referencing] @@ -100,40 +106,91 @@ struct State<'a, Message: 'a, Renderer: 'a, Event: 'a, S: 'a> { impl<'a, Message, Renderer, Event, S> Instance<'a, Message, Renderer, Event, S> where - S: Default, + S: Default + 'static, + Renderer: renderer::Renderer, { - fn rebuild_element(&self, state: &S) { - let heads = self.state.borrow_mut().take().unwrap().into_heads(); + fn diff_self(&self) { + self.with_element(|element| { + self.tree + .borrow_mut() + .borrow_mut() + .as_mut() + .unwrap() + .diff_children(std::slice::from_ref(&element)); + }); + } - *self.state.borrow_mut() = Some( - StateBuilder { - component: heads.component, - message: PhantomData, - state: PhantomData, - element_builder: |component| Some(component.view(state)), - } - .build(), - ); + fn rebuild_element_if_necessary(&self) { + let inner = self.state.borrow_mut().take().unwrap(); + if inner.borrow_element().is_none() { + let heads = inner.into_heads(); + + *self.state.borrow_mut() = Some( + StateBuilder { + component: heads.component, + message: PhantomData, + state: PhantomData, + element_builder: |component| { + Some( + component.view( + self.tree + .borrow() + .borrow() + .as_ref() + .unwrap() + .state + .downcast_ref::<S>(), + ), + ) + }, + } + .build(), + ); + self.diff_self(); + } else { + *self.state.borrow_mut() = Some(inner); + } } fn rebuild_element_with_operation( &self, - state: &mut S, operation: &mut dyn widget::Operation<Message>, ) { let heads = self.state.borrow_mut().take().unwrap().into_heads(); - heads.component.operate(state, operation); + heads.component.operate( + self.tree + .borrow_mut() + .borrow_mut() + .as_mut() + .unwrap() + .state + .downcast_mut(), + operation, + ); *self.state.borrow_mut() = Some( StateBuilder { component: heads.component, message: PhantomData, state: PhantomData, - element_builder: |component| Some(component.view(state)), + element_builder: |component| { + Some( + component.view( + self.tree + .borrow() + .borrow() + .as_ref() + .unwrap() + .state + .downcast_ref(), + ), + ) + }, } .build(), ); + self.diff_self(); } fn with_element<T>( @@ -147,6 +204,7 @@ where &self, f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T, ) -> T { + self.rebuild_element_if_necessary(); self.state .borrow_mut() .as_mut() @@ -159,27 +217,30 @@ impl<'a, Message, Renderer, Event, S> Widget<Message, Renderer> for Instance<'a, Message, Renderer, Event, S> where S: 'static + Default, - Renderer: iced_native::Renderer, + Renderer: core::Renderer, { fn tag(&self) -> tree::Tag { - struct Tag<T>(T); tree::Tag::of::<Tag<S>>() } fn state(&self) -> tree::State { - tree::State::new(S::default()) + let state = Rc::new(RefCell::new(Some(Tree { + tag: tree::Tag::of::<Tag<S>>(), + state: tree::State::new(S::default()), + children: vec![Tree::empty()], + }))); + *self.tree.borrow_mut() = state.clone(); + tree::State::new(state) } fn children(&self) -> Vec<Tree> { - self.rebuild_element(&S::default()); - self.with_element(|element| vec![Tree::new(element)]) + vec![] } fn diff(&self, tree: &mut Tree) { - self.rebuild_element(tree.state.downcast_ref()); - self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element)) - }) + let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>(); + *self.tree.borrow_mut() = tree.clone(); + self.rebuild_element_if_necessary(); } fn width(&self) -> Length { @@ -203,9 +264,9 @@ where fn on_event( &mut self, tree: &mut Tree, - event: iced_native::Event, + event: core::Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -213,12 +274,13 @@ where let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); + let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>(); let event_status = self.with_element_mut(|element| { element.as_widget_mut().on_event( - &mut tree.children[0], + &mut t.borrow_mut().as_mut().unwrap().children[0], event, layout, - cursor_position, + cursor, renderer, clipboard, &mut local_shell, @@ -235,9 +297,10 @@ where let mut heads = self.state.take().unwrap().into_heads(); for message in local_messages.into_iter().filter_map(|message| { - heads - .component - .update(tree.state.downcast_mut::<S>(), message) + heads.component.update( + t.borrow_mut().as_mut().unwrap().state.downcast_mut(), + message, + ) }) { shell.publish(message); } @@ -247,17 +310,11 @@ where component: heads.component, message: PhantomData, state: PhantomData, - element_builder: |state| { - Some(state.view(tree.state.downcast_ref::<S>())) - }, + element_builder: |_| None, } .build(), )); - self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element)) - }); - shell.invalidate_layout(); } @@ -271,10 +328,7 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation<Message>, ) { - self.rebuild_element_with_operation( - tree.state.downcast_mut(), - operation, - ); + self.rebuild_element_with_operation(operation); struct MapOperation<'a, B> { operation: &'a mut dyn widget::Operation<B>, @@ -308,13 +362,28 @@ where ) { self.operation.text_input(state, id); } + + fn scrollable( + &mut self, + state: &mut dyn widget::operation::Scrollable, + id: Option<&widget::Id>, + ) { + self.operation.scrollable(state, id); + } + + fn custom( + &mut self, + state: &mut dyn std::any::Any, + id: Option<&widget::Id>, + ) { + self.operation.custom(state, id); + } } + let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>(); self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element)); - element.as_widget().operate( - &mut tree.children[0], + &mut tree.borrow_mut().as_mut().unwrap().children[0], layout, renderer, &mut MapOperation { operation }, @@ -329,17 +398,18 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ) { + let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>(); self.with_element(|element| { element.as_widget().draw( - &tree.children[0], + &tree.borrow().as_ref().unwrap().children[0], renderer, theme, style, layout, - cursor_position, + cursor, viewport, ); }); @@ -349,15 +419,16 @@ where &self, tree: &Tree, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { + let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>(); self.with_element(|element| { element.as_widget().mouse_interaction( - &tree.children[0], + &tree.borrow().as_ref().unwrap().children[0], layout, - cursor_position, + cursor, viewport, renderer, ) @@ -370,26 +441,42 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option<overlay::Element<'b, Message, Renderer>> { - let overlay = OverlayBuilder { - instance: self, - tree, - types: PhantomData, - overlay_builder: |instance, tree| { - instance.state.get_mut().as_mut().unwrap().with_element_mut( - move |element| { - element.as_mut().unwrap().as_widget_mut().overlay( - &mut tree.children[0], - layout, - renderer, - ) - }, - ) - }, - } - .build(); + self.rebuild_element_if_necessary(); + let tree = tree + .state + .downcast_mut::<Rc<RefCell<Option<Tree>>>>() + .borrow_mut() + .take() + .unwrap(); + let overlay = Overlay(Some( + InnerBuilder { + instance: self, + tree, + types: PhantomData, + overlay_builder: |instance, tree| { + instance.state.get_mut().as_mut().unwrap().with_element_mut( + move |element| { + element + .as_mut() + .unwrap() + .as_widget_mut() + .overlay( + &mut tree.children[0], + layout, + renderer, + ) + .map(|overlay| { + RefCell::new(Nested::new(overlay)) + }) + }, + ) + }, + } + .build(), + )); - let has_overlay = overlay.with_overlay(|overlay| { - overlay.as_ref().map(overlay::Element::position) + let has_overlay = overlay.0.as_ref().unwrap().with_overlay(|overlay| { + overlay.as_ref().map(|nested| nested.borrow().position()) }); has_overlay.map(|position| { @@ -403,15 +490,29 @@ where } } +struct Overlay<'a, 'b, Message, Renderer, Event, S>( + Option<Inner<'a, 'b, Message, Renderer, Event, S>>, +); + +impl<'a, 'b, Message, Renderer, Event, S> Drop + for Overlay<'a, 'b, Message, Renderer, Event, S> +{ + fn drop(&mut self) { + if let Some(heads) = self.0.take().map(|inner| inner.into_heads()) { + *heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree); + } + } +} + #[self_referencing] -struct Overlay<'a, 'b, Message, Renderer, Event, S> { +struct Inner<'a, 'b, Message, Renderer, Event, S> { instance: &'a mut Instance<'b, Message, Renderer, Event, S>, - tree: &'a mut Tree, + tree: Tree, types: PhantomData<(Message, Event, S)>, #[borrows(mut instance, mut tree)] - #[covariant] - overlay: Option<overlay::Element<'this, Event, Renderer>>, + #[not_covariant] + overlay: Option<RefCell<Nested<'this, Event, Renderer>>>, } struct OverlayInstance<'a, 'b, Message, Renderer, Event, S> { @@ -423,31 +524,39 @@ impl<'a, 'b, Message, Renderer, Event, S> { fn with_overlay_maybe<T>( &self, - f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Event, Renderer>) -> T, ) -> Option<T> { self.overlay .as_ref() .unwrap() - .borrow_overlay() + .0 .as_ref() - .map(f) + .unwrap() + .with_overlay(|overlay| { + overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) + }) } fn with_overlay_mut_maybe<T>( &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Event, Renderer>) -> T, ) -> Option<T> { self.overlay .as_mut() .unwrap() - .with_overlay_mut(|overlay| overlay.as_mut().map(f)) + .0 + .as_mut() + .unwrap() + .with_overlay_mut(|overlay| { + overlay.as_mut().map(|nested| (f)(nested.get_mut())) + }) } } impl<'a, 'b, Message, Renderer, Event, S> overlay::Overlay<Message, Renderer> for OverlayInstance<'a, 'b, Message, Renderer, Event, S> where - Renderer: iced_native::Renderer, + Renderer: core::Renderer, S: 'static + Default, { fn layout( @@ -457,9 +566,7 @@ where position: Point, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - let translation = position - overlay.position(); - - overlay.layout(renderer, bounds, translation) + overlay.layout(renderer, bounds, position) }) .unwrap_or_default() } @@ -470,40 +577,35 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { let _ = self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, theme, style, layout, cursor_position); + overlay.draw(renderer, theme, style, layout, cursor); }); } fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.with_overlay_maybe(|overlay| { - overlay.mouse_interaction( - layout, - cursor_position, - viewport, - renderer, - ) + overlay.mouse_interaction(layout, cursor, viewport, renderer) }) .unwrap_or_default() } fn on_event( &mut self, - event: iced_native::Event, + event: core::Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - ) -> iced_native::event::Status { + ) -> event::Status { let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); @@ -512,53 +614,52 @@ where overlay.on_event( event, layout, - cursor_position, + cursor, renderer, clipboard, &mut local_shell, ) }) - .unwrap_or(iced_native::event::Status::Ignored); + .unwrap_or(event::Status::Ignored); local_shell.revalidate_layout(|| shell.invalidate_layout()); + if let Some(redraw_request) = local_shell.redraw_request() { + shell.request_redraw(redraw_request); + } + if !local_messages.is_empty() { - let overlay = self.overlay.take().unwrap().into_heads(); - let mut heads = overlay.instance.state.take().unwrap().into_heads(); + let mut inner = + self.overlay.take().unwrap().0.take().unwrap().into_heads(); + let mut heads = inner.instance.state.take().unwrap().into_heads(); for message in local_messages.into_iter().filter_map(|message| { heads .component - .update(overlay.tree.state.downcast_mut::<S>(), message) + .update(inner.tree.state.downcast_mut(), message) }) { shell.publish(message); } - *overlay.instance.state.borrow_mut() = Some( + *inner.instance.state.borrow_mut() = Some( StateBuilder { component: heads.component, message: PhantomData, state: PhantomData, - element_builder: |state| { - Some(state.view(overlay.tree.state.downcast_ref::<S>())) - }, + element_builder: |_| None, } .build(), ); - overlay.instance.with_element(|element| { - overlay.tree.diff_children(std::slice::from_ref(&element)) - }); - - self.overlay = Some( - OverlayBuilder { - instance: overlay.instance, - tree: overlay.tree, + self.overlay = Some(Overlay(Some( + InnerBuilder { + instance: inner.instance, + tree: inner.tree, types: PhantomData, overlay_builder: |_, _| None, } .build(), - ); + ))); shell.invalidate_layout(); } @@ -566,9 +667,14 @@ where event_status } - fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + fn is_over( + &self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { self.with_overlay_maybe(|overlay| { - overlay.is_over(layout, cursor_position) + overlay.is_over(layout, renderer, cursor_position) }) .unwrap_or_default() } diff --git a/lazy/src/lib.rs b/widget/src/lazy/helpers.rs index 41a28773..8ca9cb86 100644 --- a/lazy/src/lib.rs +++ b/widget/src/lazy/helpers.rs @@ -1,36 +1,11 @@ -#![doc( - html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" -)] -#![deny( - missing_debug_implementations, - unused_results, - clippy::extra_unused_lifetimes, - clippy::from_over_into, - clippy::needless_borrow, - clippy::new_without_default, - clippy::useless_conversion -)] -#![forbid(unsafe_code)] -#![allow( - clippy::await_holding_refcell_ref, - clippy::inherent_to_string, - clippy::type_complexity -)] -#![cfg_attr(docsrs, feature(doc_cfg))] -mod lazy; +use crate::core::{self, Element, Size}; +use crate::lazy::component::{self, Component}; +use crate::lazy::{Lazy, Responsive}; -pub mod component; -pub mod responsive; - -pub use component::Component; -pub use lazy::Lazy; -pub use responsive::Responsive; - -mod cache; - -use iced_native::{Element, Size}; use std::hash::Hash; +/// Creates a new [`Lazy`] widget with the given data `Dependency` and a +/// closure that can turn this data into a widget tree. pub fn lazy<'a, Message, Renderer, Dependency, View>( dependency: Dependency, view: impl Fn(&Dependency) -> View + 'a, @@ -51,16 +26,22 @@ where C: Component<Message, Renderer> + 'a, C::State: 'static, Message: 'a, - Renderer: iced_native::Renderer + 'a, + Renderer: core::Renderer + 'a, { component::view(component) } +/// Creates a new [`Responsive`] widget with a closure that produces its +/// contents. +/// +/// The `view` closure will be provided with the current [`Size`] of +/// the [`Responsive`] widget and, therefore, can be used to build the +/// contents of the widget in a responsive way. pub fn responsive<'a, Message, Renderer>( f: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a, ) -> Responsive<'a, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: core::Renderer, { Responsive::new(f) } diff --git a/lazy/src/responsive.rs b/widget/src/lazy/responsive.rs index 57c07de1..07300857 100644 --- a/lazy/src/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -1,13 +1,15 @@ -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, horizontal_space}; -use iced_native::{ - Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, +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; +use crate::core::widget::tree::{self, Tree}; +use crate::core::{ + self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; +use crate::horizontal_space; +use crate::runtime::overlay::Nested; use ouroboros::self_referencing; use std::cell::{RefCell, RefMut}; @@ -19,14 +21,14 @@ use std::ops::Deref; /// A [`Responsive`] widget will always try to fill all the available space of /// its parent. #[allow(missing_debug_implementations)] -pub struct Responsive<'a, Message, Renderer> { +pub struct Responsive<'a, Message, Renderer = crate::Renderer> { view: Box<dyn Fn(Size) -> Element<'a, Message, Renderer> + 'a>, content: RefCell<Content<'a, Message, Renderer>>, } impl<'a, Message, Renderer> Responsive<'a, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: core::Renderer, { /// Creates a new [`Responsive`] widget with a closure that produces its /// contents. @@ -41,7 +43,7 @@ where view: Box::new(view), content: RefCell::new(Content { size: Size::ZERO, - layout: layout::Node::new(Size::ZERO), + layout: None, element: Element::new(horizontal_space(0)), }), } @@ -50,18 +52,27 @@ where struct Content<'a, Message, Renderer> { size: Size, - layout: layout::Node, + layout: Option<layout::Node>, element: Element<'a, Message, Renderer>, } impl<'a, Message, Renderer> Content<'a, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: core::Renderer, { + fn layout(&mut self, renderer: &Renderer) { + if self.layout.is_none() { + self.layout = + Some(self.element.as_widget().layout( + renderer, + &layout::Limits::new(Size::ZERO, self.size), + )); + } + } + fn update( &mut self, tree: &mut Tree, - renderer: &Renderer, new_size: Size, view: &dyn Fn(Size) -> Element<'a, Message, Renderer>, ) { @@ -71,13 +82,9 @@ where self.element = view(new_size); self.size = new_size; + self.layout = None; tree.diff(&self.element); - - self.layout = self - .element - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, self.size)); } fn resolve<R, T>( @@ -96,11 +103,12 @@ where where R: Deref<Target = Renderer>, { - self.update(tree, renderer.deref(), layout.bounds().size(), view); + self.update(tree, layout.bounds().size(), view); + self.layout(renderer.deref()); let content_layout = Layout::with_offset( layout.position() - Point::ORIGIN, - &self.layout, + self.layout.as_ref().unwrap(), ); f(tree, renderer, content_layout, &mut self.element) @@ -114,7 +122,7 @@ struct State { impl<'a, Message, Renderer> Widget<Message, Renderer> for Responsive<'a, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: core::Renderer, { fn tag(&self) -> tree::Tag { tree::Tag::of::<State>() @@ -168,9 +176,9 @@ where fn on_event( &mut self, tree: &mut Tree, - event: iced_native::Event, + event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -178,7 +186,10 @@ where let state = tree.state.downcast_mut::<State>(); let mut content = self.content.borrow_mut(); - content.resolve( + let mut local_messages = vec![]; + let mut local_shell = Shell::new(&mut local_messages); + + let status = content.resolve( &mut state.tree.borrow_mut(), renderer, layout, @@ -188,13 +199,21 @@ where tree, event, layout, - cursor_position, + cursor, renderer, clipboard, - shell, + &mut local_shell, ) }, - ) + ); + + if local_shell.is_layout_invalid() { + content.layout = None; + } + + shell.merge(local_shell, std::convert::identity); + + status } fn draw( @@ -204,7 +223,7 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ) { let state = tree.state.downcast_ref::<State>(); @@ -217,13 +236,7 @@ where &self.view, |tree, renderer, layout, element| { element.as_widget().draw( - tree, - renderer, - theme, - style, - layout, - cursor_position, - viewport, + tree, renderer, theme, style, layout, cursor, viewport, ) }, ) @@ -233,7 +246,7 @@ where &self, tree: &Tree, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { @@ -246,13 +259,9 @@ where layout, &self.view, |tree, renderer, layout, element| { - element.as_widget().mouse_interaction( - tree, - layout, - cursor_position, - viewport, - renderer, - ) + element + .as_widget() + .mouse_interaction(tree, layout, cursor, viewport, renderer) }, ) } @@ -271,35 +280,32 @@ where content: self.content.borrow_mut(), tree: state.tree.borrow_mut(), types: PhantomData, - overlay_builder: |content: &mut RefMut<Content<_, _>>, tree| { - content.update( - tree, - renderer, - layout.bounds().size(), - &self.view, - ); + overlay_builder: |content: &mut RefMut<'_, Content<'_, _, _>>, + tree| { + content.update(tree, layout.bounds().size(), &self.view); + content.layout(renderer); let Content { element, - layout: content_layout, + layout: content_layout_node, .. } = content.deref_mut(); let content_layout = Layout::with_offset( layout.bounds().position() - Point::ORIGIN, - content_layout, + content_layout_node.as_ref().unwrap(), ); element .as_widget_mut() .overlay(tree, content_layout, renderer) + .map(|overlay| RefCell::new(Nested::new(overlay))) }, } .build(); - let has_overlay = overlay.with_overlay(|overlay| { - overlay.as_ref().map(overlay::Element::position) - }); + let has_overlay = + overlay.with_overlay_maybe(|overlay| overlay.position()); has_overlay .map(|position| overlay::Element::new(position, Box::new(overlay))) @@ -309,7 +315,7 @@ where impl<'a, Message, Renderer> From<Responsive<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: iced_native::Renderer + 'a, + Renderer: core::Renderer + 'a, Message: 'a, { fn from(responsive: Responsive<'a, Message, Renderer>) -> Self { @@ -324,30 +330,34 @@ struct Overlay<'a, 'b, Message, Renderer> { types: PhantomData<Message>, #[borrows(mut content, mut tree)] - #[covariant] - overlay: Option<overlay::Element<'this, Message, Renderer>>, + #[not_covariant] + overlay: Option<RefCell<Nested<'this, Message, Renderer>>>, } impl<'a, 'b, Message, Renderer> Overlay<'a, 'b, Message, Renderer> { fn with_overlay_maybe<T>( &self, - f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option<T> { - self.borrow_overlay().as_ref().map(f) + self.with_overlay(|overlay| { + overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) + }) } fn with_overlay_mut_maybe<T>( &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, + f: impl FnOnce(&mut Nested<'_, Message, Renderer>) -> T, ) -> Option<T> { - self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) + self.with_overlay_mut(|overlay| { + overlay.as_mut().map(|nested| (f)(nested.get_mut())) + }) } } impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> for Overlay<'a, 'b, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: core::Renderer, { fn layout( &self, @@ -356,9 +366,7 @@ where position: Point, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - let translation = position - overlay.position(); - - overlay.layout(renderer, bounds, translation) + overlay.layout(renderer, bounds, position) }) .unwrap_or_default() } @@ -369,56 +377,49 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { let _ = self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, theme, style, layout, cursor_position); + overlay.draw(renderer, theme, style, layout, cursor); }); } fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.with_overlay_maybe(|overlay| { - overlay.mouse_interaction( - layout, - cursor_position, - viewport, - renderer, - ) + overlay.mouse_interaction(layout, cursor, viewport, renderer) }) .unwrap_or_default() } fn on_event( &mut self, - event: iced_native::Event, + event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, 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, - ) + overlay.on_event(event, layout, cursor, renderer, clipboard, shell) }) - .unwrap_or(iced_native::event::Status::Ignored) + .unwrap_or(event::Status::Ignored) } - fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + fn is_over( + &self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { self.with_overlay_maybe(|overlay| { - overlay.is_over(layout, cursor_position) + overlay.is_over(layout, renderer, cursor_position) }) .unwrap_or_default() } |