diff options
Diffstat (limited to 'lazy/src')
-rw-r--r-- | lazy/src/cache.rs | 2 | ||||
-rw-r--r-- | lazy/src/component.rs | 367 | ||||
-rw-r--r-- | lazy/src/lib.rs | 44 | ||||
-rw-r--r-- | lazy/src/responsive.rs | 440 |
4 files changed, 485 insertions, 368 deletions
diff --git a/lazy/src/cache.rs b/lazy/src/cache.rs index 229b7912..5b4a39f6 100644 --- a/lazy/src/cache.rs +++ b/lazy/src/cache.rs @@ -9,5 +9,5 @@ pub struct Cache<'a, Message: 'a, Renderer: 'a> { #[borrows(mut element)] #[covariant] - pub overlay: Option<overlay::Element<'this, Message, Renderer>>, + overlay: Option<overlay::Element<'this, Message, Renderer>>, } diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 9e5937e9..8987b993 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -1,17 +1,17 @@ //! 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; +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 +28,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 +55,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 +64,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 +121,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 +165,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 +184,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 +198,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(); } @@ -203,28 +230,81 @@ where event_status } + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + operation: &mut dyn widget::Operation<Message>, + ) { + struct MapOperation<'a, B> { + operation: &'a mut dyn widget::Operation<B>, + } + + impl<'a, T, B> widget::Operation<T> for MapOperation<'a, B> { + fn container( + &mut self, + id: Option<&widget::Id>, + operate_on_children: &mut dyn FnMut( + &mut dyn widget::Operation<T>, + ), + ) { + self.operation.container(id, &mut |operation| { + operate_on_children(&mut MapOperation { operation }); + }); + } + + fn focusable( + &mut self, + state: &mut dyn widget::operation::Focusable, + id: Option<&widget::Id>, + ) { + self.operation.focusable(state, id); + } + } + + self.with_element(|element| { + element.as_widget().operate( + &mut tree.children[0], + layout, + &mut MapOperation { operation }, + ); + }); + } + 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.draw(renderer, style, layout, cursor_position, viewport); + 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.mouse_interaction( + element.as_widget().mouse_interaction( + &tree.children[0], layout, cursor_position, viewport, @@ -233,63 +313,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() @@ -298,27 +387,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, @@ -337,12 +420,13 @@ where fn draw( &self, renderer: &mut Renderer, + theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, ) { - self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, style, layout, cursor_position); + let _ = self.with_overlay_maybe(|overlay| { + overlay.draw(renderer, theme, style, layout, cursor_position); }); } @@ -387,39 +471,48 @@ where &mut local_shell, ) }) - .unwrap_or_else(|| iced_native::event::Status::Ignored); + .unwrap_or(iced_native::event::Status::Ignored); 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: |element| { - element.overlay(layout, renderer) - }, - } - .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 05fce765..3827746c 100644 --- a/lazy/src/lib.rs +++ b/lazy/src/lib.rs @@ -1,3 +1,22 @@ +#![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))] pub mod component; pub mod responsive; @@ -6,4 +25,27 @@ 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/responsive.rs b/lazy/src/responsive.rs index 20a80dac..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,212 +139,141 @@ 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, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { - let mut internal = self.0.borrow_mut(); - - internal.resolve(renderer, |state, renderer, content| { - content.draw( - renderer, - 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(&mut 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))) } } @@ -299,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> { @@ -308,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)) } } @@ -356,12 +337,13 @@ where fn draw( &self, renderer: &mut Renderer, + theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, ) { - self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, style, layout, cursor_position); + let _ = self.with_overlay_maybe(|overlay| { + overlay.draw(renderer, theme, style, layout, cursor_position); }); } @@ -391,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, @@ -402,6 +384,6 @@ where shell, ) }) - .unwrap_or_else(|| iced_native::event::Status::Ignored) + .unwrap_or(iced_native::event::Status::Ignored) } } |