diff options
author | 2022-07-27 06:49:20 +0200 | |
---|---|---|
committer | 2022-07-27 06:49:20 +0200 | |
commit | ff2519b1d43d481987351a83b6dd7237524c21f0 (patch) | |
tree | 5731eeb7eb1247d4a8951de0d5bc5d8102640559 /lazy | |
parent | c44267b85f7aaa2997e3caf1323b837d95818c22 (diff) | |
download | iced-ff2519b1d43d481987351a83b6dd7237524c21f0.tar.gz iced-ff2519b1d43d481987351a83b6dd7237524c21f0.tar.bz2 iced-ff2519b1d43d481987351a83b6dd7237524c21f0.zip |
Replace stateful widgets with new `iced_pure` API
Diffstat (limited to 'lazy')
-rw-r--r-- | lazy/Cargo.toml | 8 | ||||
-rw-r--r-- | lazy/src/component.rs | 308 | ||||
-rw-r--r-- | lazy/src/lib.rs | 29 | ||||
-rw-r--r-- | lazy/src/pure.rs | 31 | ||||
-rw-r--r-- | lazy/src/pure/component.rs | 479 | ||||
-rw-r--r-- | lazy/src/pure/responsive.rs | 388 | ||||
-rw-r--r-- | lazy/src/responsive.rs | 433 |
7 files changed, 406 insertions, 1270 deletions
diff --git a/lazy/Cargo.toml b/lazy/Cargo.toml index 9a184dd1..12e4e313 100644 --- a/lazy/Cargo.toml +++ b/lazy/Cargo.toml @@ -10,17 +10,9 @@ documentation = "https://docs.rs/iced_lazy" keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] -[features] -pure = ["iced_pure"] - [dependencies] ouroboros = "0.13" [dependencies.iced_native] version = "0.5" path = "../native" - -[dependencies.iced_pure] -version = "0.2" -path = "../pure" -optional = true diff --git a/lazy/src/component.rs b/lazy/src/component.rs index eac7e8ee..ea4d7311 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -1,17 +1,16 @@ //! Build and reuse custom widgets using The Elm Architecture. -use crate::{Cache, CacheBuilder}; - 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::{ Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; use ouroboros::self_referencing; -use std::cell::RefCell; +use std::cell::{Ref, RefCell}; use std::marker::PhantomData; /// A reusable, custom widget that uses The Elm Architecture. @@ -28,17 +27,24 @@ use std::marker::PhantomData; /// Additionally, a [`Component`] is capable of producing a `Message` to notify /// the parent application of any relevant interactions. pub trait Component<Message, Renderer> { + /// The internal state of this [`Component`]. + type State: Default; + /// The type of event this [`Component`] handles internally. type Event; /// Processes an [`Event`](Component::Event) and updates the [`Component`] state accordingly. /// /// It can produce a `Message` for the parent application. - fn update(&mut self, event: Self::Event) -> Option<Message>; + fn update( + &mut self, + state: &mut Self::State, + event: Self::Event, + ) -> Option<Message>; /// Produces the widgets of the [`Component`], which may trigger an [`Event`](Component::Event) /// on user interaction. - fn view(&mut self) -> Element<'_, Self::Event, Renderer>; + fn view(&self, state: &Self::State) -> Element<'_, Self::Event, Renderer>; } /// Turns an implementor of [`Component`] into an [`Element`] that can be @@ -48,6 +54,7 @@ pub fn view<'a, C, Message, Renderer>( ) -> Element<'a, Message, Renderer> where C: Component<Message, Renderer> + 'a, + C::State: 'static, Message: 'a, Renderer: iced_native::Renderer + 'a, { @@ -56,36 +63,48 @@ where StateBuilder { component: Box::new(component), message: PhantomData, - cache_builder: |state| { - Some( - CacheBuilder { - element: state.view(), - overlay_builder: |_| None, - } - .build(), - ) - }, + state: PhantomData, + element_builder: |_| None, } .build(), )), }) } -struct Instance<'a, Message, Renderer, Event> { - state: RefCell<Option<State<'a, Message, Renderer, Event>>>, +struct Instance<'a, Message, Renderer, Event, S> { + state: RefCell<Option<State<'a, Message, Renderer, Event, S>>>, } #[self_referencing] -struct State<'a, Message: 'a, Renderer: 'a, Event: 'a> { - component: Box<dyn Component<Message, Renderer, Event = Event> + 'a>, +struct State<'a, Message: 'a, Renderer: 'a, Event: 'a, S: 'a> { + component: + Box<dyn Component<Message, Renderer, Event = Event, State = S> + 'a>, message: PhantomData<Message>, + state: PhantomData<S>, - #[borrows(mut component)] + #[borrows(component)] #[covariant] - cache: Option<Cache<'this, Event, Renderer>>, + element: Option<Element<'this, Event, Renderer>>, } -impl<'a, Message, Renderer, Event> Instance<'a, Message, Renderer, Event> { +impl<'a, Message, Renderer, Event, S> Instance<'a, Message, Renderer, Event, S> +where + S: Default, +{ + fn rebuild_element(&self, state: &S) { + let heads = self.state.borrow_mut().take().unwrap().into_heads(); + + *self.state.borrow_mut() = Some( + StateBuilder { + component: heads.component, + message: PhantomData, + state: PhantomData, + element_builder: |component| Some(component.view(state)), + } + .build(), + ); + } + fn with_element<T>( &self, f: impl FnOnce(&Element<'_, Event, Renderer>) -> T, @@ -101,34 +120,43 @@ impl<'a, Message, Renderer, Event> Instance<'a, Message, Renderer, Event> { .borrow_mut() .as_mut() .unwrap() - .with_cache_mut(|cache| { - let mut element = cache.take().unwrap().into_heads().element; - let result = f(&mut element); - - *cache = Some( - CacheBuilder { - element, - overlay_builder: |_| None, - } - .build(), - ); - - result - }) + .with_element_mut(|element| f(element.as_mut().unwrap())) } } -impl<'a, Message, Renderer, Event> Widget<Message, Renderer> - for Instance<'a, Message, Renderer, Event> +impl<'a, Message, Renderer, Event, S> Widget<Message, Renderer> + for Instance<'a, Message, Renderer, Event, S> where + S: 'static + Default, Renderer: iced_native::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()) + } + + fn children(&self) -> Vec<Tree> { + self.rebuild_element(&S::default()); + self.with_element(|element| vec![Tree::new(element)]) + } + + 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)) + }) + } + fn width(&self) -> Length { - self.with_element(|element| element.width()) + self.with_element(|element| element.as_widget().width()) } fn height(&self) -> Length { - self.with_element(|element| element.height()) + self.with_element(|element| element.as_widget().height()) } fn layout( @@ -136,11 +164,14 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.with_element(|element| element.layout(renderer, limits)) + self.with_element(|element| { + element.as_widget().layout(renderer, limits) + }) } fn on_event( &mut self, + tree: &mut Tree, event: iced_native::Event, layout: Layout<'_>, cursor_position: Point, @@ -152,7 +183,8 @@ where let mut local_shell = Shell::new(&mut local_messages); let event_status = self.with_element_mut(|element| { - element.on_event( + element.as_widget_mut().on_event( + &mut tree.children[0], event, layout, cursor_position, @@ -165,37 +197,31 @@ where local_shell.revalidate_layout(|| shell.invalidate_layout()); if !local_messages.is_empty() { - let mut component = self - .state - .borrow_mut() - .take() - .unwrap() - .into_heads() - .component; - - for message in local_messages - .into_iter() - .filter_map(|message| component.update(message)) - { + 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) + }) { shell.publish(message); } - *self.state.borrow_mut() = Some( + self.state = RefCell::new(Some( StateBuilder { - component, + component: heads.component, message: PhantomData, - cache_builder: |state| { - Some( - CacheBuilder { - element: state.view(), - overlay_builder: |_| None, - } - .build(), - ) + state: PhantomData, + element_builder: |state| { + Some(state.view(tree.state.downcast_ref::<S>())) }, } .build(), - ); + )); + + self.with_element(|element| { + tree.diff_children(std::slice::from_ref(&element)) + }); shell.invalidate_layout(); } @@ -205,6 +231,7 @@ where fn draw( &self, + tree: &Tree, renderer: &mut Renderer, theme: &Renderer::Theme, style: &renderer::Style, @@ -213,7 +240,8 @@ where viewport: &Rectangle, ) { self.with_element(|element| { - element.draw( + element.as_widget().draw( + &tree.children[0], renderer, theme, style, @@ -226,13 +254,15 @@ where fn mouse_interaction( &self, + tree: &Tree, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.with_element(|element| { - element.mouse_interaction( + element.as_widget().mouse_interaction( + &tree.children[0], layout, cursor_position, viewport, @@ -241,63 +271,72 @@ where }) } - fn overlay( - &mut self, + fn overlay<'b>( + &'b self, + tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, - ) -> Option<overlay::Element<'_, Message, Renderer>> { - let has_overlay = self - .state - .borrow_mut() - .as_mut() - .unwrap() - .with_cache_mut(|cache| { - let element = cache.take().unwrap().into_heads().element; - - *cache = Some( - CacheBuilder { - element, - overlay_builder: |element| { - element.overlay(layout, renderer) - }, - } - .build(), - ); - - cache + ) -> Option<overlay::Element<'b, Message, Renderer>> { + let overlay = OverlayBuilder { + instance: self, + instance_ref_builder: |instance| instance.state.borrow(), + tree, + types: PhantomData, + overlay_builder: |instance, tree| { + instance .as_ref() .unwrap() - .borrow_overlay() + .borrow_element() .as_ref() - .map(|overlay| overlay.position()) - }); + .unwrap() + .as_widget() + .overlay(&mut tree.children[0], 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 { instance: self }), + Box::new(OverlayInstance { + overlay: Some(overlay), + }), ) }) } } -struct Overlay<'a, 'b, Message, Renderer, Event> { - instance: &'b mut Instance<'a, Message, Renderer, Event>, +#[self_referencing] +struct Overlay<'a, 'b, Message, Renderer, Event, S> { + instance: &'a Instance<'b, Message, Renderer, Event, S>, + tree: &'a mut Tree, + types: PhantomData<(Message, Event, S)>, + + #[borrows(instance)] + #[covariant] + instance_ref: Ref<'this, Option<State<'a, Message, Renderer, Event, S>>>, + + #[borrows(instance_ref, mut tree)] + #[covariant] + overlay: Option<overlay::Element<'this, Event, Renderer>>, +} + +struct OverlayInstance<'a, 'b, Message, Renderer, Event, S> { + overlay: Option<Overlay<'a, 'b, Message, Renderer, Event, S>>, } -impl<'a, 'b, Message, Renderer, Event> - Overlay<'a, 'b, Message, Renderer, Event> +impl<'a, 'b, Message, Renderer, Event, S> + OverlayInstance<'a, 'b, Message, Renderer, Event, S> { fn with_overlay_maybe<T>( &self, f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T, ) -> Option<T> { - self.instance - .state - .borrow() - .as_ref() - .unwrap() - .borrow_cache() + self.overlay .as_ref() .unwrap() .borrow_overlay() @@ -306,27 +345,21 @@ impl<'a, 'b, Message, Renderer, Event> } fn with_overlay_mut_maybe<T>( - &self, + &mut self, f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T, ) -> Option<T> { - self.instance - .state - .borrow_mut() + self.overlay .as_mut() .unwrap() - .with_cache_mut(|cache| { - cache - .as_mut() - .unwrap() - .with_overlay_mut(|overlay| overlay.as_mut().map(f)) - }) + .with_overlay_mut(|overlay| overlay.as_mut().map(f)) } } -impl<'a, 'b, Message, Renderer, Event> overlay::Overlay<Message, Renderer> - for Overlay<'a, 'b, Message, Renderer, Event> +impl<'a, 'b, Message, Renderer, Event, S> overlay::Overlay<Message, Renderer> + for OverlayInstance<'a, 'b, Message, Renderer, Event, S> where Renderer: iced_native::Renderer, + S: 'static + Default, { fn layout( &self, @@ -401,32 +434,43 @@ where local_shell.revalidate_layout(|| shell.invalidate_layout()); if !local_messages.is_empty() { - let mut component = - self.instance.state.take().unwrap().into_heads().component; - - for message in local_messages - .into_iter() - .filter_map(|message| component.update(message)) - { + let overlay = self.overlay.take().unwrap().into_heads(); + let mut heads = overlay.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) + }) { shell.publish(message); } - self.instance.state = RefCell::new(Some( + *overlay.instance.state.borrow_mut() = Some( StateBuilder { - component, + component: heads.component, message: PhantomData, - cache_builder: |state| { - Some( - CacheBuilder { - element: state.view(), - overlay_builder: |_| None, - } - .build(), - ) + state: PhantomData, + element_builder: |state| { + Some(state.view(overlay.tree.state.downcast_ref::<S>())) }, } .build(), - )); + ); + + overlay.instance.with_element(|element| { + overlay.tree.diff_children(std::slice::from_ref(&element)) + }); + + self.overlay = Some( + OverlayBuilder { + instance: overlay.instance, + instance_ref_builder: |instance| instance.state.borrow(), + tree: overlay.tree, + types: PhantomData, + overlay_builder: |_, _| None, + } + .build(), + ); shell.invalidate_layout(); } diff --git a/lazy/src/lib.rs b/lazy/src/lib.rs index aed11e9f..3827746c 100644 --- a/lazy/src/lib.rs +++ b/lazy/src/lib.rs @@ -20,13 +20,32 @@ pub mod component; pub mod responsive; -#[cfg(feature = "pure")] -#[cfg_attr(docsrs, doc(cfg(feature = "pure")))] -pub mod pure; - pub use component::Component; pub use responsive::Responsive; mod cache; -use cache::{Cache, CacheBuilder}; +use iced_native::{Element, Size}; + +/// Turns an implementor of [`Component`] into an [`Element`] that can be +/// embedded in any application. +pub fn component<'a, C, Message, Renderer>( + component: C, +) -> Element<'a, Message, Renderer> +where + C: Component<Message, Renderer> + 'a, + C::State: 'static, + Message: 'a, + Renderer: iced_native::Renderer + 'a, +{ + component::view(component) +} + +pub fn responsive<'a, Message, Renderer>( + f: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a, +) -> Responsive<'a, Message, Renderer> +where + Renderer: iced_native::Renderer, +{ + Responsive::new(f) +} diff --git a/lazy/src/pure.rs b/lazy/src/pure.rs deleted file mode 100644 index dc500e5e..00000000 --- a/lazy/src/pure.rs +++ /dev/null @@ -1,31 +0,0 @@ -mod component; -mod responsive; - -pub use component::Component; -pub use responsive::Responsive; - -use iced_native::Size; -use iced_pure::Element; - -/// Turns an implementor of [`Component`] into an [`Element`] that can be -/// embedded in any application. -pub fn component<'a, C, Message, Renderer>( - component: C, -) -> Element<'a, Message, Renderer> -where - C: Component<Message, Renderer> + 'a, - C::State: 'static, - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - component::view(component) -} - -pub fn responsive<'a, Message, Renderer>( - f: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a, -) -> Responsive<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - Responsive::new(f) -} diff --git a/lazy/src/pure/component.rs b/lazy/src/pure/component.rs deleted file mode 100644 index b414a149..00000000 --- a/lazy/src/pure/component.rs +++ /dev/null @@ -1,479 +0,0 @@ -//! 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::{Clipboard, Length, Point, Rectangle, Shell, Size}; -use iced_pure::widget::tree::{self, Tree}; -use iced_pure::{Element, Widget}; - -use ouroboros::self_referencing; -use std::cell::{Ref, RefCell}; -use std::marker::PhantomData; - -/// A reusable, custom widget that uses The Elm Architecture. -/// -/// A [`Component`] allows you to implement custom widgets as if they were -/// `iced` applications with encapsulated state. -/// -/// In other words, a [`Component`] allows you to turn `iced` applications into -/// custom widgets and embed them without cumbersome wiring. -/// -/// A [`Component`] produces widgets that may fire an [`Event`](Component::Event) -/// and update the internal state of the [`Component`]. -/// -/// Additionally, a [`Component`] is capable of producing a `Message` to notify -/// the parent application of any relevant interactions. -pub trait Component<Message, Renderer> { - /// The internal state of this [`Component`]. - type State: Default; - - /// The type of event this [`Component`] handles internally. - type Event; - - /// Processes an [`Event`](Component::Event) and updates the [`Component`] state accordingly. - /// - /// It can produce a `Message` for the parent application. - fn update( - &mut self, - state: &mut Self::State, - event: Self::Event, - ) -> Option<Message>; - - /// Produces the widgets of the [`Component`], which may trigger an [`Event`](Component::Event) - /// on user interaction. - fn view(&self, state: &Self::State) -> Element<'_, Self::Event, Renderer>; -} - -/// Turns an implementor of [`Component`] into an [`Element`] that can be -/// embedded in any application. -pub fn view<'a, C, Message, Renderer>( - component: C, -) -> Element<'a, Message, Renderer> -where - C: Component<Message, Renderer> + 'a, - C::State: 'static, - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - Element::new(Instance { - state: RefCell::new(Some( - StateBuilder { - component: Box::new(component), - message: PhantomData, - state: PhantomData, - element_builder: |_| None, - } - .build(), - )), - }) -} - -struct Instance<'a, Message, Renderer, Event, S> { - state: RefCell<Option<State<'a, Message, Renderer, Event, S>>>, -} - -#[self_referencing] -struct State<'a, Message: 'a, Renderer: 'a, Event: 'a, S: 'a> { - component: - Box<dyn Component<Message, Renderer, Event = Event, State = S> + 'a>, - message: PhantomData<Message>, - state: PhantomData<S>, - - #[borrows(component)] - #[covariant] - element: Option<Element<'this, Event, Renderer>>, -} - -impl<'a, Message, Renderer, Event, S> Instance<'a, Message, Renderer, Event, S> -where - S: Default, -{ - fn rebuild_element(&self, state: &S) { - let heads = self.state.borrow_mut().take().unwrap().into_heads(); - - *self.state.borrow_mut() = Some( - StateBuilder { - component: heads.component, - message: PhantomData, - state: PhantomData, - element_builder: |component| Some(component.view(state)), - } - .build(), - ); - } - - fn with_element<T>( - &self, - f: impl FnOnce(&Element<'_, Event, Renderer>) -> T, - ) -> T { - self.with_element_mut(|element| f(element)) - } - - fn with_element_mut<T>( - &self, - f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T, - ) -> T { - self.state - .borrow_mut() - .as_mut() - .unwrap() - .with_element_mut(|element| f(element.as_mut().unwrap())) - } -} - -impl<'a, Message, Renderer, Event, S> Widget<Message, Renderer> - for Instance<'a, Message, Renderer, Event, S> -where - S: 'static + Default, - Renderer: iced_native::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()) - } - - fn children(&self) -> Vec<Tree> { - self.rebuild_element(&S::default()); - self.with_element(|element| vec![Tree::new(element)]) - } - - 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)) - }) - } - - 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 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 { - let mut local_messages = Vec::new(); - let mut local_shell = Shell::new(&mut local_messages); - - let event_status = self.with_element_mut(|element| { - element.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout, - cursor_position, - renderer, - clipboard, - &mut local_shell, - ) - }); - - local_shell.revalidate_layout(|| shell.invalidate_layout()); - - if !local_messages.is_empty() { - 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) - }) { - shell.publish(message); - } - - self.state = RefCell::new(Some( - StateBuilder { - component: heads.component, - message: PhantomData, - state: PhantomData, - element_builder: |state| { - Some(state.view(tree.state.downcast_ref::<S>())) - }, - } - .build(), - )); - - self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element)) - }); - - shell.invalidate_layout(); - } - - event_status - } - - 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 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 overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - let overlay = OverlayBuilder { - instance: self, - instance_ref_builder: |instance| instance.state.borrow(), - tree, - types: PhantomData, - overlay_builder: |instance, tree| { - instance - .as_ref() - .unwrap() - .borrow_element() - .as_ref() - .unwrap() - .as_widget() - .overlay(&mut tree.children[0], 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(OverlayInstance { - overlay: Some(overlay), - }), - ) - }) - } -} - -#[self_referencing] -struct Overlay<'a, 'b, Message, Renderer, Event, S> { - instance: &'a Instance<'b, Message, Renderer, Event, S>, - tree: &'a mut Tree, - types: PhantomData<(Message, Event, S)>, - - #[borrows(instance)] - #[covariant] - instance_ref: Ref<'this, Option<State<'a, Message, Renderer, Event, S>>>, - - #[borrows(instance_ref, mut tree)] - #[covariant] - overlay: Option<overlay::Element<'this, Event, Renderer>>, -} - -struct OverlayInstance<'a, 'b, Message, Renderer, Event, S> { - overlay: Option<Overlay<'a, 'b, Message, Renderer, Event, S>>, -} - -impl<'a, 'b, Message, Renderer, Event, S> - OverlayInstance<'a, 'b, Message, Renderer, Event, S> -{ - fn with_overlay_maybe<T>( - &self, - f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T, - ) -> Option<T> { - self.overlay - .as_ref() - .unwrap() - .borrow_overlay() - .as_ref() - .map(f) - } - - fn with_overlay_mut_maybe<T>( - &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T, - ) -> Option<T> { - self.overlay - .as_mut() - .unwrap() - .with_overlay_mut(|overlay| overlay.as_mut().map(f)) - } -} - -impl<'a, 'b, Message, Renderer, Event, S> overlay::Overlay<Message, Renderer> - for OverlayInstance<'a, 'b, Message, Renderer, Event, S> -where - Renderer: iced_native::Renderer, - S: 'static + Default, -{ - 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>, - ) -> iced_native::event::Status { - let mut local_messages = Vec::new(); - let mut local_shell = Shell::new(&mut local_messages); - - let event_status = self - .with_overlay_mut_maybe(|overlay| { - overlay.on_event( - event, - layout, - cursor_position, - renderer, - clipboard, - &mut local_shell, - ) - }) - .unwrap_or(iced_native::event::Status::Ignored); - - local_shell.revalidate_layout(|| shell.invalidate_layout()); - - if !local_messages.is_empty() { - let overlay = self.overlay.take().unwrap().into_heads(); - let mut heads = overlay.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) - }) { - shell.publish(message); - } - - *overlay.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>())) - }, - } - .build(), - ); - - overlay.instance.with_element(|element| { - overlay.tree.diff_children(std::slice::from_ref(&element)) - }); - - self.overlay = Some( - OverlayBuilder { - instance: overlay.instance, - instance_ref_builder: |instance| instance.state.borrow(), - tree: overlay.tree, - types: PhantomData, - overlay_builder: |_, _| None, - } - .build(), - ); - - shell.invalidate_layout(); - } - - event_status - } -} diff --git a/lazy/src/pure/responsive.rs b/lazy/src/pure/responsive.rs deleted file mode 100644 index 0964ebc8..00000000 --- a/lazy/src/pure/responsive.rs +++ /dev/null @@ -1,388 +0,0 @@ -use iced_native::event; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell, Size}; -use iced_pure::horizontal_space; -use iced_pure::overlay; -use iced_pure::widget::tree::{self, Tree}; -use iced_pure::{Element, Widget}; - -use ouroboros::self_referencing; -use std::cell::{RefCell, RefMut}; -use std::marker::PhantomData; -use std::ops::Deref; - -/// A widget that is aware of its dimensions. -/// -/// 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> { - 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, -{ - /// 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 new( - view: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a, - ) -> Self { - Self { - view: Box::new(view), - content: RefCell::new(Content { - size: Size::ZERO, - layout: layout::Node::new(Size::ZERO), - element: Element::new(horizontal_space(Length::Units(0))), - }), - } - } -} - -struct Content<'a, Message, Renderer> { - size: Size, - layout: layout::Node, - element: Element<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn update( - &mut self, - tree: &mut Tree, - renderer: &Renderer, - new_size: Size, - view: &dyn Fn(Size) -> Element<'a, Message, Renderer>, - ) { - if self.size == new_size { - return; - } - - self.element = view(new_size); - self.size = new_size; - - tree.diff(&self.element); - - self.layout = self - .element - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, self.size)); - } - - fn resolve<R, T>( - &mut self, - tree: &mut Tree, - renderer: R, - layout: Layout<'_>, - view: &dyn Fn(Size) -> Element<'a, Message, Renderer>, - f: impl FnOnce( - &mut Tree, - R, - Layout<'_>, - &mut Element<'a, Message, Renderer>, - ) -> T, - ) -> T - where - R: Deref<Target = Renderer>, - { - self.update(tree, renderer.deref(), layout.bounds().size(), view); - - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - &self.layout, - ); - - f(tree, renderer, content_layout, &mut self.element) - } -} - -struct State { - tree: RefCell<Tree>, -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Responsive<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<State>() - } - - fn state(&self) -> tree::State { - tree::State::new(State { - tree: RefCell::new(Tree::empty()), - }) - } - - fn width(&self) -> Length { - Length::Fill - } - - fn height(&self) -> Length { - Length::Fill - } - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - layout::Node::new(limits.max()) - } - - 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 { - let state = tree.state.downcast_mut::<State>(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget_mut().on_event( - tree, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - let state = tree.state.downcast_ref::<State>(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget().draw( - tree, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - }, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - let state = tree.state.downcast_ref::<State>(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget().mouse_interaction( - tree, - layout, - cursor_position, - viewport, - renderer, - ) - }, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - let state = tree.state.downcast_ref::<State>(); - - let overlay = OverlayBuilder { - content: self.content.borrow_mut(), - tree: state.tree.borrow_mut(), - types: PhantomData, - overlay_builder: |content, tree| { - content.update( - tree, - renderer, - layout.bounds().size(), - &self.view, - ); - - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - &content.layout, - ); - - content.element.as_widget().overlay( - tree, - content_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))) - } -} - -impl<'a, Message, Renderer> From<Responsive<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, - Message: 'a, -{ - fn from(responsive: Responsive<'a, Message, Renderer>) -> Self { - Self::new(responsive) - } -} - -#[self_referencing] -struct Overlay<'a, 'b, Message, Renderer> { - content: RefMut<'a, Content<'b, Message, Renderer>>, - tree: RefMut<'a, Tree>, - types: PhantomData<Message>, - - #[borrows(mut content, mut tree)] - #[covariant] - overlay: Option<overlay::Element<'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, - ) -> Option<T> { - self.borrow_overlay().as_ref().map(f) - } - - fn with_overlay_mut_maybe<T>( - &mut self, - f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, - ) -> Option<T> { - self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) - } -} - -impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> - for Overlay<'a, 'b, Message, Renderer> -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) - } -} diff --git a/lazy/src/responsive.rs b/lazy/src/responsive.rs index 4a3eb5c6..0b7ae6de 100644 --- a/lazy/src/responsive.rs +++ b/lazy/src/responsive.rs @@ -1,71 +1,131 @@ -//! Build responsive widgets. -use crate::{Cache, CacheBuilder}; - -use iced_native::event::{self, Event}; +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::window; +use iced_native::widget::horizontal_space; +use iced_native::widget::tree::{self, Tree}; use iced_native::{ Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; -use std::cell::RefCell; +use ouroboros::self_referencing; +use std::cell::{RefCell, RefMut}; +use std::marker::PhantomData; use std::ops::Deref; -/// The state of a [`Responsive`] widget. -#[derive(Debug, Clone, Default)] -pub struct State { - last_size: Option<Size>, - last_layout: layout::Node, -} - -impl State { - pub fn new() -> State { - State::default() - } - - fn layout(&self, parent: Layout<'_>) -> Layout<'_> { - Layout::with_offset( - parent.position() - Point::ORIGIN, - &self.last_layout, - ) - } -} - /// A widget that is aware of its dimensions. /// /// 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>( - RefCell<Internal<'a, Message, Renderer>>, -); +pub struct Responsive<'a, Message, 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> { - /// Creates a new [`Responsive`] widget with the given [`State`] and a - /// closure that produces its contents. +impl<'a, Message, Renderer> Responsive<'a, Message, Renderer> +where + Renderer: iced_native::Renderer, +{ + /// 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 new( - state: &'a mut State, - view: impl FnOnce(Size) -> Element<'a, Message, Renderer> + 'a, + view: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a, ) -> Self { - Self(RefCell::new(Internal { - state, - content: Content::Pending(Some(Box::new(view))), - })) + Self { + view: Box::new(view), + content: RefCell::new(Content { + size: Size::ZERO, + layout: layout::Node::new(Size::ZERO), + element: Element::new(horizontal_space(Length::Units(0))), + }), + } } } +struct Content<'a, Message, Renderer> { + size: Size, + layout: layout::Node, + element: Element<'a, Message, Renderer>, +} + +impl<'a, Message, Renderer> Content<'a, Message, Renderer> +where + Renderer: iced_native::Renderer, +{ + fn update( + &mut self, + tree: &mut Tree, + renderer: &Renderer, + new_size: Size, + view: &dyn Fn(Size) -> Element<'a, Message, Renderer>, + ) { + if self.size == new_size { + return; + } + + self.element = view(new_size); + self.size = new_size; + + tree.diff(&self.element); + + self.layout = self + .element + .as_widget() + .layout(renderer, &layout::Limits::new(Size::ZERO, self.size)); + } + + fn resolve<R, T>( + &mut self, + tree: &mut Tree, + renderer: R, + layout: Layout<'_>, + view: &dyn Fn(Size) -> Element<'a, Message, Renderer>, + f: impl FnOnce( + &mut Tree, + R, + Layout<'_>, + &mut Element<'a, Message, Renderer>, + ) -> T, + ) -> T + where + R: Deref<Target = Renderer>, + { + self.update(tree, renderer.deref(), layout.bounds().size(), view); + + let content_layout = Layout::with_offset( + layout.position() - Point::ORIGIN, + &self.layout, + ); + + f(tree, renderer, content_layout, &mut self.element) + } +} + +struct State { + tree: RefCell<Tree>, +} + impl<'a, Message, Renderer> Widget<Message, Renderer> for Responsive<'a, Message, Renderer> where Renderer: iced_native::Renderer, { + fn tag(&self) -> tree::Tag { + tree::Tag::of::<State>() + } + + fn state(&self) -> tree::State { + tree::State::new(State { + tree: RefCell::new(Tree::empty()), + }) + } + fn width(&self) -> Length { Length::Fill } @@ -79,45 +139,44 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let size = limits.max(); - - self.0.borrow_mut().state.last_size = Some(size); - - layout::Node::new(size) + layout::Node::new(limits.max()) } fn on_event( &mut self, - event: Event, + tree: &mut Tree, + event: iced_native::Event, layout: Layout<'_>, cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { - let mut internal = self.0.borrow_mut(); - - if matches!(event, Event::Window(window::Event::Resized { .. })) - || internal.state.last_size - != Some(internal.state.last_layout.size()) - { - shell.invalidate_widgets(); - } - - internal.resolve(renderer, |state, renderer, content| { - content.on_event( - event, - state.layout(layout), - cursor_position, - renderer, - clipboard, - shell, - ) - }) + let state = tree.state.downcast_mut::<State>(); + let mut content = self.content.borrow_mut(); + + content.resolve( + &mut state.tree.borrow_mut(), + renderer, + layout, + &self.view, + |tree, renderer, layout, element| { + element.as_widget_mut().on_event( + tree, + event, + layout, + cursor_position, + renderer, + clipboard, + shell, + ) + }, + ) } fn draw( &self, + tree: &Tree, renderer: &mut Renderer, theme: &Renderer::Theme, style: &renderer::Style, @@ -125,168 +184,96 @@ where cursor_position: Point, viewport: &Rectangle, ) { - let mut internal = self.0.borrow_mut(); - - internal.resolve(renderer, |state, renderer, content| { - content.draw( - renderer, - theme, - style, - state.layout(layout), - cursor_position, - viewport, - ) - }) + let state = tree.state.downcast_ref::<State>(); + let mut content = self.content.borrow_mut(); + + content.resolve( + &mut state.tree.borrow_mut(), + renderer, + layout, + &self.view, + |tree, renderer, layout, element| { + element.as_widget().draw( + tree, + renderer, + theme, + style, + layout, + cursor_position, + viewport, + ) + }, + ) } fn mouse_interaction( &self, + tree: &Tree, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - let mut internal = self.0.borrow_mut(); - - internal.resolve(renderer, |state, renderer, content| { - content.mouse_interaction( - state.layout(layout), - cursor_position, - viewport, - renderer, - ) - }) + let state = tree.state.downcast_ref::<State>(); + let mut content = self.content.borrow_mut(); + + content.resolve( + &mut state.tree.borrow_mut(), + renderer, + layout, + &self.view, + |tree, renderer, layout, element| { + element.as_widget().mouse_interaction( + tree, + layout, + cursor_position, + viewport, + renderer, + ) + }, + ) } - fn overlay( - &mut self, + fn overlay<'b>( + &'b self, + tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, - ) -> Option<overlay::Element<'_, Message, Renderer>> { - let has_overlay = { - use std::ops::DerefMut; - - let mut internal = self.0.borrow_mut(); - - let _ = - internal.resolve(renderer, |_state, _renderer, _content| {}); - - let Internal { content, state } = internal.deref_mut(); - - let content_layout = state.layout(layout); - - match content { - Content::Pending(_) => None, - Content::Ready(cache) => { - *cache = Some( - CacheBuilder { - element: cache.take().unwrap().into_heads().element, - overlay_builder: |element| { - element.overlay(content_layout, renderer) - }, - } - .build(), - ); - - cache - .as_ref() - .unwrap() - .borrow_overlay() - .as_ref() - .map(|overlay| overlay.position()) - } - } - }; - - has_overlay.map(|position| { - overlay::Element::new( - position, - Box::new(Overlay { instance: self }), - ) - }) - } -} - -struct Internal<'a, Message, Renderer> { - state: &'a mut State, - content: Content<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Internal<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn resolve<R, T>( - &mut self, - renderer: R, - f: impl FnOnce(&State, R, &mut Element<'a, Message, Renderer>) -> T, - ) -> T - where - R: Deref<Target = Renderer>, - { - self.content.resolve(self.state, renderer, f) - } -} - -enum Content<'a, Message, Renderer> { - Pending( - Option<Box<dyn FnOnce(Size) -> Element<'a, Message, Renderer> + 'a>>, - ), - Ready(Option<Cache<'a, Message, Renderer>>), -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn resolve<R, T>( - &mut self, - state: &mut State, - renderer: R, - f: impl FnOnce(&State, R, &mut Element<'a, Message, Renderer>) -> T, - ) -> T - where - R: Deref<Target = Renderer>, - { - match self { - Content::Ready(cache) => { - let mut heads = cache.take().unwrap().into_heads(); - - let result = f(state, renderer, &mut heads.element); - - *cache = Some( - CacheBuilder { - element: heads.element, - overlay_builder: |_| None, - } - .build(), + ) -> Option<overlay::Element<'b, Message, Renderer>> { + let state = tree.state.downcast_ref::<State>(); + + let overlay = OverlayBuilder { + content: self.content.borrow_mut(), + tree: state.tree.borrow_mut(), + types: PhantomData, + overlay_builder: |content, tree| { + content.update( + tree, + renderer, + layout.bounds().size(), + &self.view, ); - result - } - Content::Pending(view) => { - let element = - view.take().unwrap()(state.last_size.unwrap_or(Size::ZERO)); - - state.last_layout = element.layout( - renderer.deref(), - &layout::Limits::new( - Size::ZERO, - state.last_size.unwrap_or(Size::ZERO), - ), + let content_layout = Layout::with_offset( + layout.position() - Point::ORIGIN, + &content.layout, ); - *self = Content::Ready(Some( - CacheBuilder { - element, - overlay_builder: |_| None, - } - .build(), - )); - - self.resolve(state, renderer, f) - } + content.element.as_widget().overlay( + tree, + content_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))) } } @@ -301,8 +288,15 @@ where } } +#[self_referencing] struct Overlay<'a, 'b, Message, Renderer> { - instance: &'b mut Responsive<'a, Message, Renderer>, + content: RefMut<'a, Content<'b, Message, Renderer>>, + tree: RefMut<'a, Tree>, + types: PhantomData<Message>, + + #[borrows(mut content, mut tree)] + #[covariant] + overlay: Option<overlay::Element<'this, Message, Renderer>>, } impl<'a, 'b, Message, Renderer> Overlay<'a, 'b, Message, Renderer> { @@ -310,29 +304,14 @@ impl<'a, 'b, Message, Renderer> Overlay<'a, 'b, Message, Renderer> { &self, f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, ) -> Option<T> { - let internal = self.instance.0.borrow(); - - match &internal.content { - Content::Pending(_) => None, - Content::Ready(cache) => { - cache.as_ref().unwrap().borrow_overlay().as_ref().map(f) - } - } + self.borrow_overlay().as_ref().map(f) } fn with_overlay_mut_maybe<T>( - &self, + &mut self, f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, ) -> Option<T> { - let mut internal = self.instance.0.borrow_mut(); - - match &mut internal.content { - Content::Pending(_) => None, - Content::Ready(cache) => cache - .as_mut() - .unwrap() - .with_overlay_mut(|overlay| overlay.as_mut().map(f)), - } + self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) } } @@ -394,7 +373,7 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - ) -> iced_native::event::Status { + ) -> event::Status { self.with_overlay_mut_maybe(|overlay| { overlay.on_event( event, |