From 4cd45643d7d2aa83212162f17a9b28ddae4a9340 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 25 Apr 2024 06:05:00 +0200 Subject: Introduce `opaque` widget helper --- widget/src/helpers.rs | 169 ++++++++++++++++++++++++++++++++++++++++++++- widget/src/image/viewer.rs | 2 +- widget/src/mouse_area.rs | 2 +- widget/src/scrollable.rs | 2 +- widget/src/stack.rs | 2 +- 5 files changed, 172 insertions(+), 5 deletions(-) (limited to 'widget') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 2afed3e6..fd345251 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -5,7 +5,7 @@ use crate::combo_box::{self, ComboBox}; use crate::container::{self, Container}; use crate::core; use crate::core::widget::operation; -use crate::core::{Element, Length, Pixels}; +use crate::core::{Element, Length, Pixels, Widget}; use crate::keyed; use crate::overlay; use crate::pick_list::{self, PickList}; @@ -123,6 +123,173 @@ where Stack::with_children(children) } +/// Wraps the given widget and captures any mouse button presses inside the bounds of +/// the widget—therefore making it _opaque_. +/// +/// This helper is meant to be used to mark elements in a [`Stack`] to avoid mouse +/// events from passing through layers. +/// +/// [`Stack`]: crate::Stack +pub fn opaque<'a, Message, Theme, Renderer>( + content: impl Into>, +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: core::Renderer + 'a, +{ + use crate::core::event::{self, Event}; + use crate::core::layout::{self, Layout}; + use crate::core::mouse; + use crate::core::renderer; + use crate::core::widget::tree::{self, Tree}; + use crate::core::{Rectangle, Shell, Size}; + + struct Opaque<'a, Message, Theme, Renderer> { + content: Element<'a, Message, Theme, Renderer>, + } + + impl<'a, Message, Theme, Renderer> Widget + for Opaque<'a, Message, Theme, Renderer> + where + 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 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 Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.content.as_widget().layout(tree, renderer, limits) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.content + .as_widget() + .draw(tree, renderer, theme, style, layout, cursor, viewport); + } + + fn operate( + &self, + state: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn operation::Operation, + ) { + self.content + .as_widget() + .operate(state, layout, renderer, operation); + } + + fn on_event( + &mut self, + state: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn core::Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + let is_mouse_press = matches!( + event, + core::Event::Mouse(mouse::Event::ButtonPressed(_)) + ); + + if let core::event::Status::Captured = + self.content.as_widget_mut().on_event( + state, event, layout, cursor, renderer, clipboard, shell, + viewport, + ) + { + return event::Status::Captured; + } + + if is_mouse_press && cursor.is_over(layout.bounds()) { + event::Status::Captured + } else { + event::Status::Ignored + } + } + + fn mouse_interaction( + &self, + state: &core::widget::Tree, + layout: core::Layout<'_>, + cursor: core::mouse::Cursor, + viewport: &core::Rectangle, + renderer: &Renderer, + ) -> core::mouse::Interaction { + let interaction = self + .content + .as_widget() + .mouse_interaction(state, layout, cursor, viewport, renderer); + + if interaction == mouse::Interaction::None + && cursor.is_over(layout.bounds()) + { + mouse::Interaction::Idle + } else { + interaction + } + } + + fn overlay<'b>( + &'b mut self, + state: &'b mut core::widget::Tree, + layout: core::Layout<'_>, + renderer: &Renderer, + translation: core::Vector, + ) -> Option> + { + self.content.as_widget_mut().overlay( + state, + layout, + renderer, + translation, + ) + } + } + + Element::new(Opaque { + content: content.into(), + }) +} + /// Creates a new [`Scrollable`] with the provided content. /// /// [`Scrollable`]: crate::Scrollable diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 5f7bb345..75d73b19 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -304,7 +304,7 @@ where } else if is_mouse_over { mouse::Interaction::Grab } else { - mouse::Interaction::Idle + mouse::Interaction::None } } diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 9634e477..d7235cf6 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -232,7 +232,7 @@ where ); match (self.interaction, content_interaction) { - (Some(interaction), mouse::Interaction::Idle) + (Some(interaction), mouse::Interaction::None) if cursor.is_over(layout.bounds()) => { interaction diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index cdc143a2..10e81cee 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -857,7 +857,7 @@ where if (mouse_over_x_scrollbar || mouse_over_y_scrollbar) || state.scrollers_grabbed() { - mouse::Interaction::Idle + mouse::Interaction::None } else { let translation = state.translation(self.direction, bounds, content_bounds); diff --git a/widget/src/stack.rs b/widget/src/stack.rs index 6e5dacd2..8a0ea2eb 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -249,7 +249,7 @@ where state, layout, cursor, viewport, renderer, ) }) - .find(|&interaction| interaction != mouse::Interaction::Idle) + .find(|&interaction| interaction != mouse::Interaction::None) .unwrap_or_default() } -- cgit