diff options
author | 2024-04-25 06:05:00 +0200 | |
---|---|---|
committer | 2024-04-25 06:05:37 +0200 | |
commit | 4cd45643d7d2aa83212162f17a9b28ddae4a9340 (patch) | |
tree | fbddb2c21c41a4d76c10ee0695dd5e1fd9669f14 | |
parent | 9492da11d90893b396e8dfdae47cc54c6ab42411 (diff) | |
download | iced-4cd45643d7d2aa83212162f17a9b28ddae4a9340.tar.gz iced-4cd45643d7d2aa83212162f17a9b28ddae4a9340.tar.bz2 iced-4cd45643d7d2aa83212162f17a9b28ddae4a9340.zip |
Introduce `opaque` widget helper
-rw-r--r-- | core/src/mouse/interaction.rs | 1 | ||||
-rw-r--r-- | core/src/overlay.rs | 2 | ||||
-rw-r--r-- | core/src/widget.rs | 2 | ||||
-rw-r--r-- | examples/loupe/src/main.rs | 2 | ||||
-rw-r--r-- | runtime/src/multi_window/state.rs | 2 | ||||
-rw-r--r-- | runtime/src/program/state.rs | 2 | ||||
-rw-r--r-- | widget/src/helpers.rs | 169 | ||||
-rw-r--r-- | widget/src/image/viewer.rs | 2 | ||||
-rw-r--r-- | widget/src/mouse_area.rs | 2 | ||||
-rw-r--r-- | widget/src/scrollable.rs | 2 | ||||
-rw-r--r-- | widget/src/stack.rs | 2 | ||||
-rw-r--r-- | winit/src/conversion.rs | 4 | ||||
-rw-r--r-- | winit/src/multi_window/window_manager.rs | 2 |
13 files changed, 182 insertions, 12 deletions
diff --git a/core/src/mouse/interaction.rs b/core/src/mouse/interaction.rs index 6ad66229..065eb8e7 100644 --- a/core/src/mouse/interaction.rs +++ b/core/src/mouse/interaction.rs @@ -3,6 +3,7 @@ #[allow(missing_docs)] pub enum Interaction { #[default] + None, Idle, Pointer, Grab, diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 03076a30..3a57fe16 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -79,7 +79,7 @@ where _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - mouse::Interaction::Idle + mouse::Interaction::None } /// Returns true if the cursor is over the [`Overlay`]. diff --git a/core/src/widget.rs b/core/src/widget.rs index 58a9f19b..b02e3a4f 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -137,7 +137,7 @@ where _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - mouse::Interaction::Idle + mouse::Interaction::None } /// Returns the overlay of the [`Widget`], if there is any. diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs index 6a5ff123..42b7f471 100644 --- a/examples/loupe/src/main.rs +++ b/examples/loupe/src/main.rs @@ -159,7 +159,7 @@ mod loupe { if cursor.is_over(layout.bounds()) { mouse::Interaction::ZoomIn } else { - mouse::Interaction::Idle + mouse::Interaction::None } } } diff --git a/runtime/src/multi_window/state.rs b/runtime/src/multi_window/state.rs index afd04519..10366ec0 100644 --- a/runtime/src/multi_window/state.rs +++ b/runtime/src/multi_window/state.rs @@ -48,7 +48,7 @@ where caches, queued_events: Vec::new(), queued_messages: Vec::new(), - mouse_interaction: mouse::Interaction::Idle, + mouse_interaction: mouse::Interaction::None, } } diff --git a/runtime/src/program/state.rs b/runtime/src/program/state.rs index d685b07c..c6589c22 100644 --- a/runtime/src/program/state.rs +++ b/runtime/src/program/state.rs @@ -47,7 +47,7 @@ where cache, queued_events: Vec::new(), queued_messages: Vec::new(), - mouse_interaction: mouse::Interaction::Idle, + mouse_interaction: mouse::Interaction::None, } } 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>>, +) -> 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<Message, Theme, Renderer> + 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<Tree> { + self.content.as_widget().children() + } + + fn diff(&self, tree: &mut Tree) { + self.content.as_widget().diff(tree); + } + + fn size(&self) -> Size<Length> { + self.content.as_widget().size() + } + + fn size_hint(&self) -> Size<Length> { + 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<Message>, + ) { + 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<core::overlay::Element<'b, Message, Theme, Renderer>> + { + 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() } diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index fc3d1c08..0f83dac3 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -396,7 +396,9 @@ pub fn mouse_interaction( use mouse::Interaction; match interaction { - Interaction::Idle => winit::window::CursorIcon::Default, + Interaction::None | Interaction::Idle => { + winit::window::CursorIcon::Default + } Interaction::Pointer => winit::window::CursorIcon::Pointer, Interaction::Working => winit::window::CursorIcon::Progress, Interaction::Grab => winit::window::CursorIcon::Grab, diff --git a/winit/src/multi_window/window_manager.rs b/winit/src/multi_window/window_manager.rs index 71c1688b..3a0c8556 100644 --- a/winit/src/multi_window/window_manager.rs +++ b/winit/src/multi_window/window_manager.rs @@ -60,7 +60,7 @@ where exit_on_close_request, surface, renderer, - mouse_interaction: mouse::Interaction::Idle, + mouse_interaction: mouse::Interaction::None, }, ); |