From da45b6c1627935bff5334d213096c4e78972af46 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 17 Feb 2022 19:08:54 +0700 Subject: Implement `pure::Component` in `iced_lazy` --- lazy/src/pure/component.rs | 500 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 lazy/src/pure/component.rs (limited to 'lazy/src/pure') diff --git a/lazy/src/pure/component.rs b/lazy/src/pure/component.rs new file mode 100644 index 00000000..3061e45d --- /dev/null +++ b/lazy/src/pure/component.rs @@ -0,0 +1,500 @@ +//! 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, Hasher, 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::hash::Hash; +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 { + /// 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; + + /// Produces the widgets of the [`Component`], which may trigger an [`Event`](Component::Event) + /// on user interaction. + fn view(&self, state: &Self::State) -> Element; +} + +/// 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 + '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>>, +} + +#[self_referencing] +struct State<'a, Message: 'a, Renderer: 'a, Event: 'a, S: 'a> { + component: + Box + 'a>, + message: PhantomData, + state: PhantomData, + + #[borrows(component)] + #[covariant] + element: Option>, +} + +impl<'a, Message, Renderer, Event, S> Instance<'a, Message, Renderer, Event, S> +where + S: Default, +{ + fn with_element( + &self, + f: impl FnOnce(&Element<'_, Event, Renderer>) -> T, + ) -> T { + self.with_element_mut(|element| f(element)) + } + + fn with_element_mut( + &self, + f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T, + ) -> T { + if self + .state + .borrow() + .as_ref() + .unwrap() + .borrow_element() + .is_none() + { + 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: |state| Some(state.view(&S::default())), + } + .build(), + ); + } + + self.state + .borrow_mut() + .as_mut() + .unwrap() + .with_element_mut(|element| f(element.as_mut().unwrap())) + } +} + +impl<'a, Message, Renderer, Event, S> Widget + for Instance<'a, Message, Renderer, Event, S> +where + S: 'static + Default, + Renderer: iced_native::Renderer, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(S::default()) + } + + fn children(&self) -> Vec { + self.with_element(|element| vec![Tree::new(element)]) + } + + fn diff(&self, tree: &mut Tree) { + 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::(), 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::())) + }, + } + .build(), + )); + + shell.invalidate_layout(); + } + + event_status + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + ) { + self.with_element(|element| { + element.as_widget().draw( + &tree.children[0], + renderer, + style, + layout, + cursor_position, + viewport, + ); + }); + } + + fn hash_layout(&self, state: &mut Hasher) { + self.with_element(|element| { + element.as_widget().hash_layout(state); + }); + } + + 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> { + 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>>, + + #[borrows(instance_ref, mut tree)] + #[covariant] + overlay: Option>, +} + +struct OverlayInstance<'a, 'b, Message, Renderer, Event, S> { + overlay: Option>, +} + +impl<'a, 'b, Message, Renderer, Event, S> + OverlayInstance<'a, 'b, Message, Renderer, Event, S> +{ + fn with_overlay_maybe( + &self, + f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T, + ) -> Option { + self.overlay + .as_ref() + .unwrap() + .borrow_overlay() + .as_ref() + .map(f) + } + + fn with_overlay_mut_maybe( + &mut self, + f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T, + ) -> Option { + self.overlay + .as_mut() + .unwrap() + .with_overlay_mut(|overlay| overlay.as_mut().map(f)) + } +} + +impl<'a, 'b, Message, Renderer, Event, S> overlay::Overlay + 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, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + ) { + self.with_overlay_maybe(|overlay| { + overlay.draw(renderer, 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 hash_layout(&self, state: &mut Hasher, position: Point) { + struct Marker; + std::any::TypeId::of::().hash(state); + + (position.x as u32).hash(state); + (position.y as u32).hash(state); + + self.with_overlay_maybe(|overlay| { + overlay.hash_layout(state); + }); + } + + 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_else(|| 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::(), 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::())) + }, + } + .build(), + ); + + self.overlay = Some( + OverlayBuilder { + instance: overlay.instance, + instance_ref_builder: |instance| instance.state.borrow(), + tree: overlay.tree, + types: PhantomData, + overlay_builder: |instance, tree| { + instance + .as_ref() + .unwrap() + .borrow_element() + .as_ref() + .unwrap() + .as_widget() + .overlay(tree, layout, renderer) + }, + } + .build(), + ); + + shell.invalidate_layout(); + } + + event_status + } +} -- cgit From fa3bd4280e7c34354db0a15ef1baba9fbbeb87f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 7 Mar 2022 16:33:25 +0700 Subject: Initialize `lazy::pure::Component` view properly ... during `children` and `diff` in the `Widget` implementation --- lazy/src/pure/component.rs | 47 +++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) (limited to 'lazy/src/pure') diff --git a/lazy/src/pure/component.rs b/lazy/src/pure/component.rs index c79e34f8..ad82c55e 100644 --- a/lazy/src/pure/component.rs +++ b/lazy/src/pure/component.rs @@ -101,27 +101,6 @@ where &self, f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T, ) -> T { - if self - .state - .borrow() - .as_ref() - .unwrap() - .borrow_element() - .is_none() - { - 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: |state| Some(state.view(&S::default())), - } - .build(), - ); - } - self.state .borrow_mut() .as_mut() @@ -145,10 +124,36 @@ where } fn children(&self) -> Vec { + 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: |state| Some(state.view(&S::default())), + } + .build(), + ); + self.with_element(|element| vec![Tree::new(element)]) } fn diff(&self, tree: &mut Tree) { + 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: |state| { + Some(state.view(tree.state.downcast_ref())) + }, + } + .build(), + ); + self.with_element(|element| { tree.diff_children(std::slice::from_ref(&element)) }) -- cgit From 9fd66c820d08d3ff193ef04907686774c98d2fec Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 7 Mar 2022 16:39:04 +0700 Subject: Introduce `rebuild_element` helper in `lazy::pure::Component` --- lazy/src/pure/component.rs | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) (limited to 'lazy/src/pure') diff --git a/lazy/src/pure/component.rs b/lazy/src/pure/component.rs index ad82c55e..4d952f69 100644 --- a/lazy/src/pure/component.rs +++ b/lazy/src/pure/component.rs @@ -90,6 +90,20 @@ 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( &self, f: impl FnOnce(&Element<'_, Event, Renderer>) -> T, @@ -124,36 +138,12 @@ where } fn children(&self) -> Vec { - 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: |state| Some(state.view(&S::default())), - } - .build(), - ); - + self.rebuild_element(&S::default()); self.with_element(|element| vec![Tree::new(element)]) } fn diff(&self, tree: &mut Tree) { - 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: |state| { - Some(state.view(tree.state.downcast_ref())) - }, - } - .build(), - ); - + self.rebuild_element(tree.state.downcast_ref()); self.with_element(|element| { tree.diff_children(std::slice::from_ref(&element)) }) -- cgit From b50e208f31e51c2d947aeaf9fea8a9f287f26aa1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 7 Mar 2022 18:04:13 +0700 Subject: Implement `pure::Responsive` in `iced_lazy` --- lazy/src/pure/responsive.rs | 365 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 lazy/src/pure/responsive.rs (limited to 'lazy/src/pure') diff --git a/lazy/src/pure/responsive.rs b/lazy/src/pure/responsive.rs new file mode 100644 index 00000000..2b77873f --- /dev/null +++ b/lazy/src/pure/responsive.rs @@ -0,0 +1,365 @@ +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::overlay; +use iced_pure::widget::horizontal_space; +use iced_pure::widget::tree::{self, Tree}; +use iced_pure::{Element, Widget}; + +use ouroboros::self_referencing; +use std::cell::{Ref, 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 Element<'a, Message, Renderer> + 'a>, + content: RefCell>, +} + +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> { + 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; + self.layout = self + .element + .as_widget() + .layout(renderer, &layout::Limits::new(Size::ZERO, self.size)); + + tree.diff(&self.element); + } + + fn resolve( + &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, + { + 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, +} + +impl<'a, Message, Renderer> Widget + for Responsive<'a, Message, Renderer> +where + Renderer: iced_native::Renderer, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + 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::(); + 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, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + ) { + let state = tree.state.downcast_ref::(); + 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, + 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::(); + 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> { + let state = tree.state.downcast_ref::(); + + let overlay = OverlayBuilder { + content: self.content.borrow(), + tree: state.tree.borrow_mut(), + types: PhantomData, + overlay_builder: |content, tree| { + content.element.as_widget().overlay(tree, layout, renderer) + }, + } + .build(); + + let has_overlay = overlay.with_overlay(|overlay| { + overlay.as_ref().map(overlay::Element::position) + }); + + has_overlay + .map(|position| overlay::Element::new(position, Box::new(overlay))) + } +} + +impl<'a, Message, Renderer> From> + 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: Ref<'a, Content<'b, Message, Renderer>>, + tree: RefMut<'a, Tree>, + types: PhantomData, + + #[borrows(content, mut tree)] + #[covariant] + overlay: Option>, +} + +impl<'a, 'b, Message, Renderer> Overlay<'a, 'b, Message, Renderer> { + fn with_overlay_maybe( + &self, + f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T, + ) -> Option { + self.borrow_overlay().as_ref().map(f) + } + + fn with_overlay_mut_maybe( + &mut self, + f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T, + ) -> Option { + self.with_overlay_mut(|overlay| overlay.as_mut().map(f)) + } +} + +impl<'a, 'b, Message, Renderer> overlay::Overlay + 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, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + ) { + self.with_overlay_maybe(|overlay| { + overlay.draw(renderer, 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_else(|| iced_native::event::Status::Ignored) + } +} -- cgit From 9f27969d14232355ad628431fb67aa07e42e768f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 14 Mar 2022 17:41:21 +0700 Subject: Fix incorrect `layout` in `Widget::overlay` for `pure::Responsive` --- lazy/src/pure/responsive.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'lazy/src/pure') diff --git a/lazy/src/pure/responsive.rs b/lazy/src/pure/responsive.rs index 2b77873f..291f1aaf 100644 --- a/lazy/src/pure/responsive.rs +++ b/lazy/src/pure/responsive.rs @@ -9,7 +9,7 @@ use iced_pure::widget::tree::{self, Tree}; use iced_pure::{Element, Widget}; use ouroboros::self_referencing; -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::{RefCell, RefMut}; use std::marker::PhantomData; use std::ops::Deref; @@ -236,11 +236,27 @@ where let state = tree.state.downcast_ref::(); let overlay = OverlayBuilder { - content: self.content.borrow(), + content: self.content.borrow_mut(), tree: state.tree.borrow_mut(), types: PhantomData, overlay_builder: |content, tree| { - content.element.as_widget().overlay(tree, layout, renderer) + 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(); @@ -267,11 +283,11 @@ where #[self_referencing] struct Overlay<'a, 'b, Message, Renderer> { - content: Ref<'a, Content<'b, Message, Renderer>>, + content: RefMut<'a, Content<'b, Message, Renderer>>, tree: RefMut<'a, Tree>, types: PhantomData, - #[borrows(content, mut tree)] + #[borrows(mut content, mut tree)] #[covariant] overlay: Option>, } -- cgit From d7100fd2597da82d97eaf196d50573ea64f3f8ff Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 16 Mar 2022 17:37:19 +0700 Subject: Export widget modules in `iced_pure` ... and fix collisions with the new `helpers` --- lazy/src/pure/responsive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lazy/src/pure') diff --git a/lazy/src/pure/responsive.rs b/lazy/src/pure/responsive.rs index 291f1aaf..2b62a047 100644 --- a/lazy/src/pure/responsive.rs +++ b/lazy/src/pure/responsive.rs @@ -3,8 +3,8 @@ 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::horizontal_space; use iced_pure::widget::tree::{self, Tree}; use iced_pure::{Element, Widget}; -- cgit