diff --git a/core/src/element.rs b/core/src/element.rs index 6f8751ab..9d083d79 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -366,7 +366,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, @@ -518,7 +518,7 @@ where fn overlay<'b>( &'b mut self, state: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 92118567..90d5731a 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -120,7 +120,7 @@ where pub fn from_children<'a, Message, Theme, Renderer>( children: &'a mut [crate::Element<'_, Message, Theme, Renderer>], tree: &'a mut Tree, - layout: Layout<'_>, + layout: Layout<'a>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/core/src/transformation.rs b/core/src/transformation.rs index 74183147..d9b805ae 100644 --- a/core/src/transformation.rs +++ b/core/src/transformation.rs @@ -40,6 +40,11 @@ impl Transformation { pub fn translation(&self) -> Vector { Vector::new(self.0.w_axis.x, self.0.w_axis.y) } + + /// Returns the inverse of the [`Transformation`]. + pub fn inverse(self) -> Self { + Transformation(self.0.inverse()) + } } impl Default for Transformation { diff --git a/core/src/widget.rs b/core/src/widget.rs index 72985e3e..655c010b 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -144,7 +144,7 @@ where fn overlay<'a>( &'a mut self, _state: &'a mut Tree, - _layout: Layout<'_>, + _layout: Layout<'a>, _renderer: &Renderer, _viewport: &Rectangle, _translation: Vector, diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index bbf4ac7d..a40df370 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -9,8 +9,8 @@ use crate::civitai::{Error, Id, Image, Rgba, Size}; use iced::animation; use iced::time::{Instant, milliseconds}; use iced::widget::{ - button, container, grid, horizontal_space, image, mouse_area, opaque, pop, - scrollable, stack, + button, container, float, grid, horizontal_space, image, mouse_area, + opaque, pop, scrollable, stack, }; use iced::window; use iced::{ @@ -204,28 +204,28 @@ fn card<'a>( let image = if let Some(preview) = preview { let thumbnail: Element<'_, _> = if let Preview::Ready { thumbnail, .. } = &preview { - image(&thumbnail.handle) - .width(Fill) - .content_fit(ContentFit::Cover) - .opacity(thumbnail.fade_in.interpolate(0.0, 1.0, now)) - .scale(thumbnail.zoom.interpolate(1.0, 1.1, now)) - .translate(move |bounds, viewport| { - bounds.zoom(1.1).offset(&viewport.shrink(10)) - * thumbnail.zoom.interpolate(0.0, 1.0, now) - }) - .style(move |_theme| image::Style { - shadow: Shadow { - color: Color::BLACK.scale_alpha( - thumbnail.zoom.interpolate(0.0, 1.0, now), - ), - blur_radius: thumbnail - .zoom - .interpolate(0.0, 20.0, now), - ..Shadow::default() - }, - ..image::Style::default() - }) - .into() + float( + image(&thumbnail.handle) + .width(Fill) + .content_fit(ContentFit::Cover) + .opacity(thumbnail.fade_in.interpolate(0.0, 1.0, now)), + ) + .scale(thumbnail.zoom.interpolate(1.0, 1.1, now)) + .translate(move |bounds, viewport| { + bounds.zoom(1.1).offset(&viewport.shrink(10)) + * thumbnail.zoom.interpolate(0.0, 1.0, now) + }) + .style(move |_theme| float::Style { + shadow: Shadow { + color: Color::BLACK.scale_alpha( + thumbnail.zoom.interpolate(0.0, 1.0, now), + ), + blur_radius: thumbnail.zoom.interpolate(0.0, 20.0, now), + ..Shadow::default() + }, + ..float::Style::default() + }) + .into() } else { horizontal_space().into() }; diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index ef9418d8..a58fb03a 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -423,7 +423,7 @@ mod toast { fn overlay<'b>( &'b mut self, state: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/button.rs b/widget/src/button.rs index 63987597..d084cd61 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -430,7 +430,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/column.rs b/widget/src/column.rs index 777eb328..4ab56c89 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -336,7 +336,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/container.rs b/widget/src/container.rs index 1c774ced..7b9c8bf7 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -377,7 +377,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/float.rs b/widget/src/float.rs new file mode 100644 index 00000000..a5f893d9 --- /dev/null +++ b/widget/src/float.rs @@ -0,0 +1,370 @@ +#![allow(missing_docs)] +use crate::core; +use crate::core::border; +use crate::core::layout; +use crate::core::mouse; +use crate::core::overlay; +use crate::core::renderer; +use crate::core::widget; +use crate::core::widget::tree; +use crate::core::{ + Clipboard, Element, Event, Layout, Length, Point, Rectangle, Shadow, Shell, + Size, Transformation, Vector, Widget, +}; + +#[allow(missing_debug_implementations)] +pub struct Float<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> +where + Theme: Catalog, +{ + content: Element<'a, Message, Theme, Renderer>, + scale: f32, + translate: Option Vector + 'a>>, + opaque: bool, + class: Theme::Class<'a>, +} + +impl<'a, Message, Theme, Renderer> Float<'a, Message, Theme, Renderer> +where + Theme: Catalog, +{ + pub fn new( + content: impl Into>, + ) -> Self { + Self { + content: content.into(), + scale: 1.0, + translate: None, + opaque: false, + class: Theme::default(), + } + } + + pub fn scale(mut self, scale: f32) -> Self { + self.scale = scale; + self + } + + pub fn translate( + mut self, + translate: impl Fn(Rectangle, Rectangle) -> Vector + 'a, + ) -> Self { + self.translate = Some(Box::new(translate)); + self + } + + pub fn opaque(mut self, opaque: bool) -> Self { + self.opaque = opaque; + self + } + + /// Sets the style of the [`Float`]. + #[must_use] + pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self + where + Theme::Class<'a>: From>, + { + self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); + self + } + + /// Sets the style class of the [`Float`]. + #[cfg(feature = "advanced")] + #[must_use] + pub fn class(mut self, class: impl Into>) -> Self { + self.class = class.into(); + self + } + + fn is_floating(&self, bounds: Rectangle, viewport: Rectangle) -> bool { + self.scale > 1.0 + || self.translate.as_ref().is_some_and(|translate| { + translate(bounds, viewport) != Vector::ZERO + }) + } +} + +impl Widget + for Float<'_, Message, Theme, Renderer> +where + Theme: Catalog, + Renderer: core::Renderer, +{ + fn tag(&self) -> tree::Tag { + self.content.as_widget().tag() + } + + fn state(&self) -> tree::State { + self.content.as_widget().state() + } + + fn children(&self) -> Vec { + self.content.as_widget().children() + } + + fn diff(&self, tree: &mut widget::Tree) { + self.content.as_widget().diff(tree); + } + + fn size(&self) -> Size { + self.content.as_widget().size() + } + + fn size_hint(&self) -> Size { + self.content.as_widget().size_hint() + } + + fn layout( + &self, + tree: &mut widget::Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.content.as_widget().layout(tree, renderer, limits) + } + + fn update( + &mut self, + state: &mut widget::Tree, + event: &Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { + self.content.as_widget_mut().update( + state, event, layout, cursor, renderer, clipboard, shell, viewport, + ); + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + if self.is_floating(layout.bounds(), *viewport) { + return; + } + + { + let style = theme.style(&self.class); + + if style.shadow.color.a > 0.0 { + renderer.fill_quad( + renderer::Quad { + bounds: layout.bounds().shrink(1.0), + shadow: style.shadow, + border: border::rounded(style.shadow_border_radius), + }, + style.shadow.color, + ); + } + } + + self.content + .as_widget() + .draw(tree, renderer, theme, style, layout, cursor, viewport); + } + + fn mouse_interaction( + &self, + state: &widget::Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content + .as_widget() + .mouse_interaction(state, layout, cursor, viewport, renderer) + } + + fn operate( + &self, + state: &mut widget::Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + self.content + .as_widget() + .operate(state, layout, renderer, operation); + } + + fn overlay<'a>( + &'a mut self, + state: &'a mut widget::Tree, + layout: Layout<'a>, + renderer: &Renderer, + viewport: &Rectangle, + offset: Vector, + ) -> Option> { + let bounds = layout.bounds(); + + let translation = self + .translate + .as_ref() + .map(|translate| translate(bounds + offset, *viewport)) + .unwrap_or(Vector::ZERO); + + if self.scale > 1.0 || translation != Vector::ZERO { + Some(overlay::Element::new(Box::new(Overlay { + float: self, + state, + layout, + viewport: *viewport, + bounds, + translation: translation + offset, + }))) + } else { + self.content + .as_widget_mut() + .overlay(state, layout, renderer, viewport, offset) + } + } +} + +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + 'a, + Renderer: core::Renderer + 'a, +{ + fn from(float: Float<'a, Message, Theme, Renderer>) -> Self { + Element::new(float) + } +} + +struct Overlay<'a, 'b, Message, Theme, Renderer> +where + Theme: Catalog, +{ + float: &'a Float<'b, Message, Theme, Renderer>, + state: &'a mut widget::Tree, + layout: Layout<'a>, + viewport: Rectangle, + bounds: Rectangle, + translation: Vector, +} + +impl core::Overlay + for Overlay<'_, '_, Message, Theme, Renderer> +where + Theme: Catalog, + Renderer: core::Renderer, +{ + fn layout(&mut self, _renderer: &Renderer, _bounds: Size) -> layout::Node { + layout::Node::new(self.bounds.size()).move_to(self.bounds.position()) + } + + fn is_over( + &self, + layout: Layout<'_>, + _renderer: &Renderer, + cursor_position: Point, + ) -> bool { + self.float.opaque && layout.bounds().contains(cursor_position) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + let bounds = layout.bounds(); + + let transformation = Transformation::translate( + bounds.x + bounds.width / 2.0 + self.translation.x, + bounds.y + bounds.height / 2.0 + self.translation.y, + ) * Transformation::scale(self.float.scale) + * Transformation::translate( + -bounds.x - bounds.width / 2.0, + -bounds.y - bounds.height / 2.0, + ); + + renderer.with_layer(self.viewport, |renderer| { + renderer.with_transformation(transformation, |renderer| { + { + let style = theme.style(&self.float.class); + + if style.shadow.color.a > 0.0 { + renderer.fill_quad( + renderer::Quad { + bounds: bounds.shrink(1.0), + shadow: style.shadow, + border: border::rounded( + style.shadow_border_radius, + ), + }, + style.shadow.color, + ); + } + } + + self.float.content.as_widget().draw( + self.state, + renderer, + theme, + style, + self.layout, + cursor, + &(self.viewport * transformation.inverse()), + ); + }); + }); + } + + fn index(&self) -> f32 { + self.float.scale * 0.5 + } +} + +/// The theme catalog of a [`Float`]. +/// +/// All themes that can be used with [`Float`] +/// must implement this trait. +pub trait Catalog { + /// The item class of the [`Catalog`]. + type Class<'a>; + + /// The default class produced by the [`Catalog`]. + fn default<'a>() -> Self::Class<'a>; + + /// The [`Style`] of a class with the given status. + fn style(&self, class: &Self::Class<'_>) -> Style; +} + +/// A styling function for a [`Float`]. +pub type StyleFn<'a, Theme> = Box Style + 'a>; + +impl Catalog for crate::Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> Self::Class<'a> { + Box::new(|_| Style::default()) + } + + fn style(&self, class: &Self::Class<'_>) -> Style { + class(self) + } +} + +/// The style of a [`Float`]. +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct Style { + /// The [`Shadow`] of the [`Float`]. + pub shadow: Shadow, + /// The border radius of the shadow. + pub shadow_border_radius: border::Radius, +} diff --git a/widget/src/grid.rs b/widget/src/grid.rs index 76de415d..4a08dc55 100644 --- a/widget/src/grid.rs +++ b/widget/src/grid.rs @@ -343,7 +343,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 82908f88..9e8d5f6c 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -7,6 +7,7 @@ use crate::core; use crate::core::widget::operation::{self, Operation}; use crate::core::window; use crate::core::{Element, Length, Pixels, Widget}; +use crate::float::{self, Float}; use crate::keyed; use crate::overlay; use crate::pane_grid::{self, PaneGrid}; @@ -692,7 +693,7 @@ where fn overlay<'b>( &'b mut self, state: &'b mut core::widget::Tree, - layout: core::Layout<'_>, + layout: core::Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: core::Vector, @@ -948,7 +949,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut core::widget::Tree, - layout: core::Layout<'_>, + layout: core::Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: core::Vector, @@ -1848,12 +1849,7 @@ where /// ``` /// #[cfg(feature = "image")] -pub fn image<'a, Handle, Theme>( - handle: impl Into, -) -> crate::Image<'a, Handle, Theme> -where - Theme: crate::image::Catalog, -{ +pub fn image(handle: impl Into) -> crate::Image { crate::Image::new(handle.into()) } @@ -2127,3 +2123,14 @@ where { PaneGrid::new(state, view) } + +/// Creates a new [`Float`] widget with the given content. +pub fn float<'a, Message, Theme, Renderer>( + content: impl Into>, +) -> Float<'a, Message, Theme, Renderer> +where + Theme: float::Catalog, + Renderer: core::Renderer, +{ + Float::new(content) +} diff --git a/widget/src/image.rs b/widget/src/image.rs index fa40f441..e94213a0 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -19,17 +19,14 @@ pub mod viewer; pub use viewer::Viewer; -use crate::core; -use crate::core::border; use crate::core::image; use crate::core::layout; use crate::core::mouse; -use crate::core::overlay; use crate::core::renderer; use crate::core::widget::Tree; use crate::core::{ - ContentFit, Element, Layout, Length, Point, Rectangle, Rotation, Shadow, - Size, Vector, Widget, + ContentFit, Element, Layout, Length, Point, Rectangle, Rotation, Size, + Vector, Widget, }; pub use image::{FilterMethod, Handle}; @@ -58,10 +55,7 @@ pub fn viewer(handle: Handle) -> Viewer { /// ``` /// #[allow(missing_debug_implementations)] -pub struct Image<'a, Handle = image::Handle, Theme = crate::Theme> -where - Theme: Catalog, -{ +pub struct Image { handle: Handle, width: Length, height: Length, @@ -70,14 +64,9 @@ where rotation: Rotation, opacity: f32, scale: f32, - translate: Option Vector + 'a>>, - class: Theme::Class<'a>, } -impl<'a, Handle, Theme> Image<'a, Handle, Theme> -where - Theme: Catalog, -{ +impl Image { /// Creates a new [`Image`] with the given path. pub fn new(handle: impl Into) -> Self { Image { @@ -89,8 +78,6 @@ where rotation: Rotation::default(), opacity: 1.0, scale: 1.0, - translate: None, - class: Theme::default(), } } @@ -143,40 +130,6 @@ where self.scale = scale.into(); self } - - /// Sets the translation that should be applied to an [`Image`], potentially making it - /// float above other content. - /// - /// This method takes a closure that will receive the non-scaled bounds of the [`Image`] - /// and the bounds of the viewport. The closure must produce a [`Vector`] representing - /// the translation to be applied. - /// - /// Translating can be useful to ensure images stay visible inside the viewport. - pub fn translate( - mut self, - translate: impl Fn(Rectangle, Rectangle) -> Vector + 'a, - ) -> Self { - self.translate = Some(Box::new(translate)); - self - } - - /// Sets the style of the [`Image`]. - #[must_use] - pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self - where - Theme::Class<'a>: From>, - { - self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); - self - } - - /// Sets the style class of the [`Image`]. - #[cfg(feature = "advanced")] - #[must_use] - pub fn class(mut self, class: impl Into>) -> Self { - self.class = class.into(); - self - } } /// Computes the layout of an [`Image`]. @@ -273,8 +226,6 @@ pub fn draw( rotation: Rotation, opacity: f32, scale: f32, - translate: Option<&dyn Fn(Rectangle, Rectangle) -> Vector>, - style: Style, ) where Renderer: image::Renderer, Handle: Clone, @@ -284,12 +235,6 @@ pub fn draw( drawing_bounds(renderer, bounds, handle, content_fit, rotation, scale); if must_clip(bounds, drawing_bounds) { - if translate.is_some_and(|translate| { - scale > 1.0 || translate(bounds, *viewport) != Vector::ZERO - }) { - return; - } - if let Some(bounds) = bounds.intersection(viewport) { renderer.with_layer(bounds, |renderer| { render( @@ -312,17 +257,6 @@ pub fn draw( drawing_bounds, ); } - - if style.shadow.color.a > 0.0 { - renderer.fill_quad( - renderer::Quad { - bounds: bounds.shrink(1.0), - shadow: style.shadow, - border: border::rounded(style.shadow_border_radius), - }, - style.shadow.color, - ); - } } fn render( @@ -349,11 +283,10 @@ fn render( } impl Widget - for Image<'_, Handle, Theme> + for Image where Renderer: image::Renderer, Handle: Clone, - Theme: Catalog, { fn size(&self) -> Size { Size { @@ -383,7 +316,7 @@ where &self, _state: &Tree, renderer: &mut Renderer, - theme: &Theme, + _theme: &Theme, _style: &renderer::Style, layout: Layout<'_>, _cursor: mouse::Cursor, @@ -399,183 +332,17 @@ where self.rotation, self.opacity, self.scale, - self.translate.as_deref(), - theme.style(&self.class), ); } - - fn overlay<'a>( - &'a mut self, - _state: &'a mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - viewport: &Rectangle, - translation: Vector, - ) -> Option> { - let translate = self.translate.as_ref()?; - let bounds = layout.bounds() + translation; - let drawing_bounds = drawing_bounds( - renderer, - bounds, - &self.handle, - self.content_fit, - self.rotation, - self.scale, - ); - - if must_clip(bounds, drawing_bounds) { - let translate = translate(bounds, *viewport); - - if self.scale <= 1.0 && translate == Vector::ZERO { - return None; - } - - Some(overlay::Element::new(Box::new(Overlay { - image: self, - viewport: *viewport, - clip_bounds: bounds + translate, - drawing_bounds: drawing_bounds + translate, - }))) - } else { - None - } - } } -impl<'a, Message, Theme, Renderer, Handle> From> +impl<'a, Message, Theme, Renderer, Handle> From> for Element<'a, Message, Theme, Renderer> where Renderer: image::Renderer, Handle: Clone + 'a, - Theme: Catalog + 'a, { - fn from( - image: Image<'a, Handle, Theme>, - ) -> Element<'a, Message, Theme, Renderer> { + fn from(image: Image) -> Element<'a, Message, Theme, Renderer> { Element::new(image) } } - -/// The theme catalog of an [`Image`]. -/// -/// All themes that can be used with [`Image`] -/// must implement this trait. -pub trait Catalog { - /// The item class of the [`Catalog`]. - type Class<'a>; - - /// The default class produced by the [`Catalog`]. - fn default<'a>() -> Self::Class<'a>; - - /// The [`Style`] of a class with the given status. - fn style(&self, class: &Self::Class<'_>) -> Style; -} - -/// A styling function for an [`Image`]. -pub type StyleFn<'a, Theme> = Box Style + 'a>; - -impl Catalog for crate::Theme { - type Class<'a> = StyleFn<'a, Self>; - - fn default<'a>() -> Self::Class<'a> { - Box::new(|_| Style::default()) - } - - fn style(&self, class: &Self::Class<'_>) -> Style { - class(self) - } -} - -/// The style of an [`Image`]. -#[derive(Debug, Clone, Copy, PartialEq, Default)] -pub struct Style { - /// The [`Shadow`] of the [`Image`]. - pub shadow: Shadow, - /// The border radius of the shadow. - pub shadow_border_radius: border::Radius, -} - -struct Overlay<'a, 'b, Handle, Theme> -where - Theme: Catalog, -{ - image: &'a Image<'b, Handle, Theme>, - viewport: Rectangle, - clip_bounds: Rectangle, - drawing_bounds: Rectangle, -} - -impl core::Overlay - for Overlay<'_, '_, Handle, Theme> -where - Renderer: image::Renderer, - Handle: Clone, - Theme: Catalog, -{ - fn layout(&mut self, _renderer: &Renderer, _bounds: Size) -> layout::Node { - layout::Node::new(self.clip_bounds.size()) - .move_to(self.clip_bounds.position()) - } - - fn is_over( - &self, - _layout: Layout<'_>, - _renderer: &Renderer, - _cursor_position: Point, - ) -> bool { - false - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - _style: &renderer::Style, - layout: Layout<'_>, - _cursor: mouse::Cursor, - ) { - let bounds = layout.bounds(); - let clip_bounds = bounds.zoom(self.image.scale); - - let Some(clip_bounds) = clip_bounds.intersection(&self.viewport) else { - return; - }; - - let style = theme.style(&self.image.class); - - if style.shadow.color.a > 0.0 { - renderer.with_layer( - clip_bounds.expand(style.shadow.blur_radius), - |renderer| { - renderer.fill_quad( - renderer::Quad { - bounds: self - .drawing_bounds - .intersection(&clip_bounds) - .unwrap_or(self.drawing_bounds) - .shrink(1.0), - shadow: style.shadow, - border: border::rounded(style.shadow_border_radius), - }, - style.shadow.color, - ); - }, - ); - } - - renderer.with_layer(clip_bounds, |renderer| { - render( - renderer, - &self.image.handle, - self.image.filter_method, - self.image.rotation, - self.image.opacity, - self.drawing_bounds, - ); - }); - } - - fn index(&self) -> f32 { - self.image.scale * 0.5 - } -} diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index ed9b5dbf..a774c239 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -367,7 +367,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 82fab287..eb8ccb12 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -265,7 +265,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, @@ -281,10 +281,11 @@ where .take() .unwrap(), tree: &mut tree.children[0], - overlay_builder: |element, tree| { + layout, + overlay_builder: |element, tree, layout| { element .as_widget_mut() - .overlay(tree, layout, renderer, viewport, translation) + .overlay(tree, *layout, renderer, viewport, translation) .map(|overlay| RefCell::new(Nested::new(overlay))) }, } @@ -312,8 +313,9 @@ struct Inner<'a, Message: 'a, Theme: 'a, Renderer: 'a> { cell: Rc>>>, element: Element<'static, Message, Theme, Renderer>, tree: &'a mut Tree, + layout: Layout<'a>, - #[borrows(mut element, mut tree)] + #[borrows(mut element, mut tree, layout)] #[not_covariant] overlay: Option>>, } diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index d6984109..4b9833c3 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -445,7 +445,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index ca5825e3..f02c119e 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -50,6 +50,7 @@ where content: RefCell::new(Content { size: Size::ZERO, layout: None, + is_layout_invalid: true, element: Element::new(horizontal_space().width(0)), }), } @@ -59,6 +60,7 @@ where struct Content<'a, Message, Theme, Renderer> { size: Size, layout: Option, + is_layout_invalid: bool, element: Element<'a, Message, Theme, Renderer>, } @@ -67,12 +69,13 @@ where Renderer: core::Renderer, { fn layout(&mut self, tree: &mut Tree, renderer: &Renderer) { - if self.layout.is_none() { + if self.layout.is_none() || self.is_layout_invalid { self.layout = Some(self.element.as_widget().layout( tree, renderer, &layout::Limits::new(Size::ZERO, self.size), )); + self.is_layout_invalid = false; } } @@ -281,7 +284,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, @@ -305,6 +308,7 @@ where let Content { element, layout: content_layout_node, + is_layout_invalid, .. } = content.deref_mut(); @@ -324,7 +328,7 @@ where translation, ) .map(|overlay| RefCell::new(Nested::new(overlay))), - content_layout_node, + is_layout_invalid, ) }, } @@ -361,7 +365,7 @@ struct Overlay<'a, 'b, Message, Theme, Renderer> { #[not_covariant] overlay: ( Option>>, - &'this mut Option, + &'this mut bool, ), } @@ -440,7 +444,7 @@ where if is_layout_invalid { self.with_overlay_mut(|(_overlay, layout)| { - **layout = None; + **layout = true; }); } } diff --git a/widget/src/lib.rs b/widget/src/lib.rs index dcaea007..f39f081a 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -20,6 +20,7 @@ pub mod button; pub mod checkbox; pub mod combo_box; pub mod container; +pub mod float; pub mod grid; pub mod keyed; pub mod overlay; @@ -60,6 +61,8 @@ pub use combo_box::ComboBox; #[doc(no_inline)] pub use container::Container; #[doc(no_inline)] +pub use float::Float; +#[doc(no_inline)] pub use grid::Grid; #[doc(no_inline)] pub use mouse_area::MouseArea; diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 54387e2d..6367698a 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -294,7 +294,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 743eec0a..8ab296e8 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -977,7 +977,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 8a235be7..8800d13b 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -364,7 +364,7 @@ where pub(crate) fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index 714f7f47..5cd53b50 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -568,7 +568,7 @@ where pub(crate) fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/pin.rs b/widget/src/pin.rs index 2b663d1e..539bf2c1 100644 --- a/widget/src/pin.rs +++ b/widget/src/pin.rs @@ -242,7 +242,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut widget::Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/pop.rs b/widget/src/pop.rs index 75973e3c..a736b434 100644 --- a/widget/src/pop.rs +++ b/widget/src/pop.rs @@ -293,7 +293,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: core::Layout<'_>, + layout: core::Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: core::Vector, diff --git a/widget/src/row.rs b/widget/src/row.rs index 1de29e17..a51106d1 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -335,7 +335,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, @@ -546,7 +546,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 9d704d40..72019137 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -1178,7 +1178,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/stack.rs b/widget/src/stack.rs index dda9c357..5e10ff2c 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -352,7 +352,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/themer.rs b/widget/src/themer.rs index 3d58db61..82b65486 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -178,7 +178,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector, diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 4223c8b6..ab6b8f14 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -271,7 +271,7 @@ where fn overlay<'b>( &'b mut self, tree: &'b mut widget::Tree, - layout: Layout<'_>, + layout: Layout<'b>, renderer: &Renderer, viewport: &Rectangle, translation: Vector,