diff --git a/core/src/element.rs b/core/src/element.rs index b7d51aeb..6f8751ab 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -368,12 +368,13 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let mapper = &self.mapper; self.widget - .overlay(tree, layout, renderer, translation) + .overlay(tree, layout, renderer, viewport, translation) .map(move |overlay| overlay.map(mapper)) } } @@ -519,10 +520,15 @@ where state: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { - self.element - .widget - .overlay(state, layout, renderer, translation) + self.element.widget.overlay( + state, + layout, + renderer, + viewport, + translation, + ) } } diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 49d4c39e..92118567 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -122,6 +122,7 @@ pub fn from_children<'a, Message, Theme, Renderer>( tree: &'a mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> where @@ -132,9 +133,13 @@ where .zip(&mut tree.children) .zip(layout.children()) .filter_map(|((child, state), layout)| { - child - .as_widget_mut() - .overlay(state, layout, renderer, translation) + child.as_widget_mut().overlay( + state, + layout, + renderer, + viewport, + translation, + ) }) .collect::>(); diff --git a/core/src/widget.rs b/core/src/widget.rs index 807b14ac..72985e3e 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -146,6 +146,7 @@ where _state: &'a mut Tree, _layout: Layout<'_>, _renderer: &Renderer, + _viewport: &Rectangle, _translation: Vector, ) -> Option> { None diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 47653686..4b8af044 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -212,11 +212,9 @@ fn card<'a>( .scale(thumbnail.zoom.interpolate(1.0, 1.1, now)) .translate(move |bounds, viewport| { let final_bounds = bounds.zoom(1.1); - final_bounds.offset(&viewport.shrink(10)) * thumbnail.zoom.interpolate(0.0, 1.0, now) }) - .float(true) .style(move |_theme| image::Style { shadow: Shadow { color: Color::BLACK.scale_alpha( diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 87d4f107..ef9418d8 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -425,6 +425,7 @@ mod toast { state: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let instants = state.state.downcast_mut::>>(); @@ -435,6 +436,7 @@ mod toast { &mut content_state[0], layout, renderer, + viewport, translation, ); diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 9b396c69..88cab5e4 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -190,6 +190,7 @@ where let mut outdated = false; let mut redraw_request = window::RedrawRequest::Wait; let mut input_method = InputMethod::Disabled; + let viewport = Rectangle::with_size(self.bounds); let mut manual_overlay = ManuallyDrop::new( self.root @@ -198,6 +199,7 @@ where &mut self.state, Layout::new(&self.base), renderer, + &viewport, Vector::ZERO, ) .map(overlay::Nested::new), @@ -242,6 +244,7 @@ where &mut self.state, Layout::new(&self.base), renderer, + &viewport, Vector::ZERO, ) .map(overlay::Nested::new), @@ -443,6 +446,7 @@ where &mut self.state, Layout::new(&self.base), renderer, + &viewport, Vector::ZERO, ) .map(overlay::Nested::new) @@ -513,6 +517,7 @@ where &mut self.state, Layout::new(base), renderer, + &viewport, Vector::ZERO, ) .map(overlay::Nested::new) @@ -558,6 +563,8 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation, ) { + let viewport = Rectangle::with_size(self.bounds); + self.root.as_widget().operate( &mut self.state, Layout::new(&self.base), @@ -572,6 +579,7 @@ where &mut self.state, Layout::new(&self.base), renderer, + &viewport, Vector::ZERO, ) .map(overlay::Nested::new) diff --git a/widget/src/button.rs b/widget/src/button.rs index d4500888..63987597 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -432,12 +432,14 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], layout.children().next().unwrap(), renderer, + viewport, translation, ) } diff --git a/widget/src/column.rs b/widget/src/column.rs index 7200690b..777eb328 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -338,6 +338,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { overlay::from_children( @@ -345,6 +346,7 @@ where tree, layout, renderer, + viewport, translation, ) } diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index f71e4a6e..195fe0cb 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -832,6 +832,7 @@ where tree: &'b mut widget::Tree, layout: Layout<'_>, _renderer: &Renderer, + _viewport: &Rectangle, translation: Vector, ) -> Option> { let is_focused = { diff --git a/widget/src/container.rs b/widget/src/container.rs index 82774186..1c774ced 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -379,12 +379,14 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { self.content.as_widget_mut().overlay( tree, layout.children().next().unwrap(), renderer, + viewport, translation, ) } diff --git a/widget/src/grid.rs b/widget/src/grid.rs index da827007..76de415d 100644 --- a/widget/src/grid.rs +++ b/widget/src/grid.rs @@ -345,6 +345,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { overlay::from_children( @@ -352,6 +353,7 @@ where tree, layout, renderer, + viewport, translation, ) } diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 6dfa660b..82908f88 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -694,6 +694,7 @@ where state: &'b mut core::widget::Tree, layout: core::Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: core::Vector, ) -> Option> { @@ -701,6 +702,7 @@ where state, layout, renderer, + viewport, translation, ) } @@ -948,6 +950,7 @@ where tree: &'b mut core::widget::Tree, layout: core::Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: core::Vector, ) -> Option> { @@ -959,6 +962,7 @@ where tree, layout, renderer, + viewport, translation, ) }); diff --git a/widget/src/image.rs b/widget/src/image.rs index 5aaf98c7..5a7bfb01 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -71,7 +71,6 @@ where opacity: f32, scale: f32, translate: Option Vector + 'a>>, - float: bool, class: Theme::Class<'a>, } @@ -91,7 +90,6 @@ where opacity: 1.0, scale: 1.0, translate: None, - float: false, class: Theme::default(), } } @@ -146,14 +144,14 @@ where self } - /// Sets the translation to apply to the [`Image`] when floating. + /// 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 floating images stay visible inside the - /// viewport. + /// Translating can be useful to ensure images stay visible inside the viewport. pub fn translate( mut self, translate: impl Fn(Rectangle, Rectangle) -> Vector + 'a, @@ -162,18 +160,6 @@ where self } - /// Sets whether an [`Image`] should float above other content when - /// scaled up. - /// - /// By default, an [`Image`] has this flag set to `false`; meaning it - /// will be clipped or "framed" inside its bounds. - /// - /// Enabling this flag is useful to create cool hover effects! - pub fn float(mut self, float: bool) -> Self { - self.float = float; - self - } - /// Sets the style of the [`Image`]. #[must_use] pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self @@ -287,7 +273,7 @@ pub fn draw( rotation: Rotation, opacity: f32, scale: f32, - float: bool, + translate: Option<&dyn Fn(Rectangle, Rectangle) -> Vector>, style: Style, ) where Renderer: image::Renderer, @@ -298,7 +284,9 @@ pub fn draw( drawing_bounds(renderer, bounds, handle, content_fit, rotation, scale); if must_clip(bounds, drawing_bounds) { - if scale > 1.0 && float { + if translate.is_some_and(|translate| { + scale > 1.0 || translate(bounds, *viewport) != Vector::ZERO + }) { return; } @@ -411,7 +399,7 @@ where self.rotation, self.opacity, self.scale, - self.float, + self.translate.as_deref(), theme.style(&self.class), ); } @@ -421,12 +409,10 @@ where _state: &'a mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { - if !self.float || self.scale <= 1.0 { - return None; - } - + let translate = self.translate.as_ref()?; let bounds = layout.bounds() + translation; let drawing_bounds = drawing_bounds( renderer, @@ -438,10 +424,17 @@ where ); 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, - clip_bounds: bounds, - drawing_bounds, + viewport: *viewport, + clip_bounds: bounds + translate, + drawing_bounds: drawing_bounds + translate, }))) } else { None @@ -507,6 +500,7 @@ where Theme: Catalog, { image: &'a Image<'b, Handle, Theme>, + viewport: Rectangle, clip_bounds: Rectangle, drawing_bounds: Rectangle, } @@ -518,18 +512,9 @@ where Handle: Clone, Theme: Catalog, { - fn layout(&mut self, _renderer: &Renderer, bounds: Size) -> layout::Node { - let bounds = if let Some(translate) = &self.image.translate { - self.clip_bounds - + translate( - self.clip_bounds, - Rectangle::new(Point::ORIGIN, bounds), - ) - } else { - self.clip_bounds - }; - - layout::Node::new(bounds.size()).move_to(bounds.position()) + 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( @@ -550,7 +535,6 @@ where _cursor: mouse::Cursor, ) { let bounds = layout.bounds(); - let translation = bounds.position() - self.clip_bounds.position(); let clip_bounds = bounds.zoom(self.image.scale); let style = theme.style(&self.image.class); @@ -560,7 +544,11 @@ where |renderer| { renderer.fill_quad( renderer::Quad { - bounds: clip_bounds.shrink(1.0), + 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), }, @@ -570,6 +558,10 @@ where ); } + let clip_bounds = clip_bounds + .intersection(&self.viewport) + .unwrap_or(self.viewport); + renderer.with_layer(clip_bounds, |renderer| { render( renderer, @@ -577,7 +569,7 @@ where self.image.filter_method, self.image.rotation, self.image.opacity, - self.drawing_bounds + translation, + self.drawing_bounds, ); }); } diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 3064a8c4..ed9b5dbf 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -369,6 +369,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { overlay::from_children( @@ -376,6 +377,7 @@ where tree, layout, renderer, + viewport, translation, ) } diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 8b7b38ce..82fab287 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -267,6 +267,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let overlay = InnerBuilder { @@ -283,7 +284,7 @@ where overlay_builder: |element, tree| { element .as_widget_mut() - .overlay(tree, layout, renderer, translation) + .overlay(tree, layout, renderer, viewport, translation) .map(|overlay| RefCell::new(Nested::new(overlay))) }, } diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index 0cfcc953..d6984109 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -447,6 +447,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { self.rebuild_element_if_necessary(); @@ -469,6 +470,7 @@ where &mut tree.children[0], layout, renderer, + viewport, translation, ) .map(|overlay| RefCell::new(Nested::new(overlay))) diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index e7c937af..ca5825e3 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -283,6 +283,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { use std::ops::DerefMut; @@ -315,7 +316,13 @@ where ( element .as_widget_mut() - .overlay(tree, content_layout, renderer, translation) + .overlay( + tree, + content_layout, + renderer, + viewport, + translation, + ) .map(|overlay| RefCell::new(Nested::new(overlay))), content_layout_node, ) diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 2ea0b059..54387e2d 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -296,12 +296,14 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], layout, renderer, + viewport, translation, ) } diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index db93c724..743eec0a 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -979,6 +979,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let children = self @@ -997,7 +998,7 @@ where return None; } - content.overlay(state, layout, renderer, translation) + content.overlay(state, layout, renderer, viewport, translation) }) .collect::>(); diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 4d63dd18..8a235be7 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -366,6 +366,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { if let Some(title_bar) = self.title_bar.as_mut() { @@ -380,6 +381,7 @@ where title_bar_state, title_bar_layout, renderer, + viewport, translation, ) { Some(overlay) => Some(overlay), @@ -387,6 +389,7 @@ where body_state, children.next()?, renderer, + viewport, translation, ), } @@ -395,6 +398,7 @@ where &mut tree.children[0], layout, renderer, + viewport, translation, ) } diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index 611c3d67..714f7f47 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -570,6 +570,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let mut children = layout.children(); @@ -588,7 +589,7 @@ where content .as_widget_mut() - .overlay(title_state, title_layout, renderer, translation) + .overlay(title_state, title_layout, renderer, viewport, translation) .or_else(move || { controls.as_mut().and_then(|controls| { let controls_layout = children.next()?; @@ -605,6 +606,7 @@ where compact_state, compact_layout, renderer, + viewport, translation, ) } else { @@ -612,6 +614,7 @@ where controls_state, controls_layout, renderer, + viewport, translation, ) } @@ -620,6 +623,7 @@ where controls_state, controls_layout, renderer, + viewport, translation, ) } diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 653c2f04..4a9d1e2f 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -690,6 +690,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + _viewport: &Rectangle, translation: Vector, ) -> Option> { let state = tree.state.downcast_mut::>(); diff --git a/widget/src/pin.rs b/widget/src/pin.rs index afa29398..2b663d1e 100644 --- a/widget/src/pin.rs +++ b/widget/src/pin.rs @@ -244,12 +244,14 @@ where tree: &'b mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { self.content.as_widget_mut().overlay( tree, layout.children().next().unwrap(), renderer, + viewport, translation, ) } diff --git a/widget/src/pop.rs b/widget/src/pop.rs index 5add1525..75973e3c 100644 --- a/widget/src/pop.rs +++ b/widget/src/pop.rs @@ -295,12 +295,14 @@ where tree: &'b mut Tree, layout: core::Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: core::Vector, ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], layout, renderer, + viewport, translation, ) } diff --git a/widget/src/row.rs b/widget/src/row.rs index b9fd2569..1de29e17 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -337,6 +337,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { overlay::from_children( @@ -344,6 +345,7 @@ where tree, layout, renderer, + viewport, translation, ) } @@ -546,9 +548,11 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { - self.row.overlay(tree, layout, renderer, translation) + self.row + .overlay(tree, layout, renderer, viewport, translation) } } diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index d50591a1..9d704d40 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -1180,11 +1180,13 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let bounds = layout.bounds(); let content_layout = layout.children().next().unwrap(); let content_bounds = content_layout.bounds(); + let visible_bounds = bounds.intersection(viewport).unwrap_or(*viewport); let offset = tree.state.downcast_ref::().translation( self.direction, @@ -1196,6 +1198,7 @@ where &mut tree.children[0], layout.children().next().unwrap(), renderer, + &visible_bounds, translation - offset, ) } diff --git a/widget/src/stack.rs b/widget/src/stack.rs index df9f6162..dda9c357 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -354,6 +354,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { overlay::from_children( @@ -361,6 +362,7 @@ where tree, layout, renderer, + viewport, translation, ) } diff --git a/widget/src/themer.rs b/widget/src/themer.rs index eb7f861c..3d58db61 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -180,6 +180,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { struct Overlay<'a, Message, Theme, NewTheme, Renderer> { @@ -287,7 +288,7 @@ where self.content .as_widget_mut() - .overlay(tree, layout, renderer, translation) + .overlay(tree, layout, renderer, viewport, translation) .map(|content| Overlay { to_theme: &self.to_theme, content, diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index ce34f2a5..4223c8b6 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -273,6 +273,7 @@ where tree: &'b mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let state = tree.state.downcast_ref::(); @@ -283,6 +284,7 @@ where children.next().unwrap(), layout, renderer, + viewport, translation, );