From 3029481b72a2ca975a9bb915e23c0461334ba54f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 04:34:13 +0100 Subject: Ignore redraws of invisible content in `hover` widget --- core/src/shell.rs | 12 ++++++++++++ widget/src/helpers.rs | 14 +++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/core/src/shell.rs b/core/src/shell.rs index 12ebbaa8..c2275f71 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -80,6 +80,18 @@ impl<'a, Message> Shell<'a, Message> { self.redraw_request } + /// Replaces the redraw request of the [`Shell`]; without conflict resolution. + /// + /// This is useful if you want to overwrite the redraw request to a previous value. + /// Since it's a fairly advanced use case and should rarely be used, it is a static + /// method. + pub fn replace_redraw_request( + shell: &mut Self, + redraw_request: Option, + ) { + shell.redraw_request = redraw_request; + } + /// Returns whether the current layout is invalid or not. pub fn is_layout_invalid(&self) -> bool { self.is_layout_invalid diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 17cf94cc..199b8fc0 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -871,16 +871,19 @@ where shell.request_redraw(); } + let is_visible = + is_hovered || self.is_top_focused || self.is_top_overlay_active; + if matches!( event, Event::Mouse( mouse::Event::CursorMoved { .. } | mouse::Event::ButtonReleased(_) ) - ) || is_hovered - || self.is_top_focused - || self.is_top_overlay_active + ) || is_visible { + let redraw_request = shell.redraw_request(); + self.top.as_widget_mut().update( top_tree, event.clone(), @@ -891,6 +894,11 @@ where shell, viewport, ); + + // Ignore redraw requests of invisible content + if !is_visible { + Shell::replace_redraw_request(shell, redraw_request); + } }; if shell.is_event_captured() { -- cgit From 0c0651de5b1722194d48c19cd645e747fb72ccb6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 05:45:51 +0100 Subject: Introduce `Levitating` variant for `mouse::Cursor` --- core/src/mouse/cursor.rs | 39 ++++++++++++++++++++++++++++++++------- widget/src/scrollable.rs | 6 ++++-- widget/src/stack.rs | 21 +++++++++------------ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/core/src/mouse/cursor.rs b/core/src/mouse/cursor.rs index 616cd315..9388a578 100644 --- a/core/src/mouse/cursor.rs +++ b/core/src/mouse/cursor.rs @@ -1,13 +1,14 @@ use crate::{Point, Rectangle, Transformation, Vector}; -use std::ops::Mul; - /// The mouse cursor state. #[derive(Debug, Clone, Copy, PartialEq, Default)] pub enum Cursor { /// The cursor has a defined position. Available(Point), + /// The cursor has a defined position, but it's levitating over a layer above. + Levitating(Point), + /// The cursor is currently unavailable (i.e. out of bounds or busy). #[default] Unavailable, @@ -18,7 +19,7 @@ impl Cursor { pub fn position(self) -> Option { match self { Cursor::Available(position) => Some(position), - Cursor::Unavailable => None, + Cursor::Levitating(_) | Cursor::Unavailable => None, } } @@ -51,17 +52,41 @@ impl Cursor { pub fn is_over(self, bounds: Rectangle) -> bool { self.position_over(bounds).is_some() } + + /// Returns true if the [`Cursor`] is levitating over a layer above. + pub fn is_levitating(self) -> bool { + matches!(self, Self::Levitating(_)) + } + + /// Makes the [`Cursor`] levitate over a layer above. + pub fn levitate(self) -> Self { + match self { + Self::Available(position) => Self::Levitating(position), + _ => self, + } + } + + /// Brings the [`Cursor`] back to the current layer. + pub fn land(self) -> Self { + match self { + Cursor::Levitating(position) => Cursor::Available(position), + _ => self, + } + } } -impl Mul for Cursor { +impl std::ops::Mul for Cursor { type Output = Self; fn mul(self, transformation: Transformation) -> Self { match self { - Cursor::Unavailable => Cursor::Unavailable, - Cursor::Available(point) => { - Cursor::Available(point * transformation) + Self::Available(position) => { + Self::Available(position * transformation) + } + Self::Levitating(position) => { + Self::Levitating(position * transformation) } + Self::Unavailable => Self::Unavailable, } } } diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 312aee29..2dbe0479 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -563,7 +563,8 @@ where Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { if let Some(scrollbar) = scrollbars.y { - let Some(cursor_position) = cursor.position() + let Some(cursor_position) = + cursor.land().position() else { return; }; @@ -635,7 +636,8 @@ where match event { Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { - let Some(cursor_position) = cursor.position() else { + let Some(cursor_position) = cursor.land().position() + else { return; }; diff --git a/widget/src/stack.rs b/widget/src/stack.rs index 12ed941d..d5cf1ecf 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -216,15 +216,15 @@ where viewport: &Rectangle, ) { let is_over = cursor.is_over(layout.bounds()); - let is_mouse_movement = - matches!(event, Event::Mouse(mouse::Event::CursorMoved { .. })); + let end = self.children.len() - 1; - for ((child, state), layout) in self + for (i, ((child, state), layout)) in self .children .iter_mut() .rev() .zip(tree.children.iter_mut().rev()) .zip(layout.children().rev()) + .enumerate() { child.as_widget_mut().update( state, @@ -237,22 +237,19 @@ where viewport, ); - if is_over - && !is_mouse_movement - && cursor != mouse::Cursor::Unavailable - { + if shell.is_event_captured() { + return; + } + + if i < end && is_over && !cursor.is_levitating() { let interaction = child.as_widget().mouse_interaction( state, layout, cursor, viewport, renderer, ); if interaction != mouse::Interaction::None { - cursor = mouse::Cursor::Unavailable; + cursor = cursor.levitate(); } } - - if shell.is_event_captured() { - return; - } } } -- cgit