From dfc1868179d96236ddf2a9eb590832d810afb6c3 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 12 Dec 2022 01:53:45 +0100 Subject: [PATCH 01/10] feat(native): Add MouseListener widget --- native/src/widget.rs | 3 + native/src/widget/helpers.rs | 10 + native/src/widget/mouse_listener.rs | 403 ++++++++++++++++++++++++++++ src/widget.rs | 8 + 4 files changed, 424 insertions(+) create mode 100644 native/src/widget/mouse_listener.rs diff --git a/native/src/widget.rs b/native/src/widget.rs index 2b3ca7be07..4eb3d1ba04 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -17,6 +17,7 @@ pub mod column; pub mod container; pub mod helpers; pub mod image; +pub mod mouse_listener; pub mod operation; pub mod pane_grid; pub mod pick_list; @@ -51,6 +52,8 @@ pub use helpers::*; #[doc(no_inline)] pub use image::Image; #[doc(no_inline)] +pub use mouse_listener::MouseListener; +#[doc(no_inline)] pub use pane_grid::PaneGrid; #[doc(no_inline)] pub use pick_list::PickList; diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index b25e064db3..88986d6086 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -314,3 +314,13 @@ where { widget::Svg::new(handle) } + +/// A container intercepting mouse events. +pub fn mouse_listener<'a, Message, Renderer>( + widget: impl Into>, +) -> widget::MouseListener<'a, Message, Renderer> +where + Renderer: crate::Renderer, +{ + widget::MouseListener::new(widget) +} diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs new file mode 100644 index 0000000000..0add7c7188 --- /dev/null +++ b/native/src/widget/mouse_listener.rs @@ -0,0 +1,403 @@ +//! A container for capturing mouse events. + +use crate::event::{self, Event}; +use crate::layout; +use crate::mouse; +use crate::overlay; +use crate::renderer; +use crate::touch; +use crate::widget::{tree, Operation, Tree}; +use crate::{ + Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Widget, +}; + +use std::u32; + +/// Emit messages on mouse events. +#[allow(missing_debug_implementations)] +pub struct MouseListener<'a, Message, Renderer> { + content: Element<'a, Message, Renderer>, + + /// Sets the message to emit on a left mouse button press. + on_press: Option, + + /// Sets the message to emit on a left mouse button release. + on_release: Option, + + /// Sets the message to emit on a right mouse button press. + on_right_press: Option, + + /// Sets the message to emit on a right mouse button release. + on_right_release: Option, + + /// Sets the message to emit on a middle mouse button press. + on_middle_press: Option, + + /// Sets the message to emit on a middle mouse button release. + on_middle_release: Option, + + /// Sets the message to emit when the mouse enters the widget. + on_mouse_enter: Option, + + /// Sets the messsage to emit when the mouse exits the widget. + on_mouse_exit: Option, +} + +impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { + /// The message to emit on a left button press. + #[must_use] + pub fn on_press(mut self, message: Message) -> Self { + self.on_press = Some(message); + self + } + + /// The message to emit on a left button release. + #[must_use] + pub fn on_release(mut self, message: Message) -> Self { + self.on_release = Some(message); + self + } + + /// The message to emit on a right button press. + #[must_use] + pub fn on_right_press(mut self, message: Message) -> Self { + self.on_right_press = Some(message); + self + } + + /// The message to emit on a right button release. + #[must_use] + pub fn on_right_release(mut self, message: Message) -> Self { + self.on_right_release = Some(message); + self + } + + /// The message to emit on a middle button press. + #[must_use] + pub fn on_middle_press(mut self, message: Message) -> Self { + self.on_middle_press = Some(message); + self + } + + /// The message to emit on a middle button release. + #[must_use] + pub fn on_middle_release(mut self, message: Message) -> Self { + self.on_middle_release = Some(message); + self + } + + /// The message to emit when the mouse enters the widget. + #[must_use] + pub fn on_mouse_enter(mut self, message: Message) -> Self { + self.on_mouse_enter = Some(message); + self + } + + /// The messsage to emit when the mouse exits the widget. + #[must_use] + pub fn on_mouse_exit(mut self, message: Message) -> Self { + self.on_mouse_exit = Some(message); + self + } +} + +/// Local state of the [`MouseListener`]. +#[derive(Default)] +struct State { + hovered: bool, +} + +impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { + /// Creates an empty [`MouseListener`]. + pub fn new(content: impl Into>) -> Self { + MouseListener { + content: content.into(), + on_press: None, + on_release: None, + on_right_press: None, + on_right_release: None, + on_middle_press: None, + on_middle_release: None, + on_mouse_enter: None, + on_mouse_exit: None, + } + } +} + +impl<'a, Message, Renderer> Widget + for MouseListener<'a, Message, Renderer> +where + Renderer: crate::Renderer, + Message: Clone, +{ + fn children(&self) -> Vec { + vec![Tree::new(&self.content)] + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(std::slice::from_ref(&self.content)); + } + + fn width(&self) -> Length { + self.content.as_widget().width() + } + + fn height(&self) -> Length { + self.content.as_widget().height() + } + + fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + layout( + renderer, + limits, + Widget::::width(self), + Widget::::height(self), + u32::MAX, + u32::MAX, + |renderer, limits| { + self.content.as_widget().layout(renderer, limits) + }, + ) + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + operation.container(None, &mut |operation| { + self.content.as_widget().operate( + &mut tree.children[0], + layout.children().next().unwrap(), + renderer, + operation, + ); + }); + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + if let event::Status::Captured = self.content.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + layout.children().next().unwrap(), + cursor_position, + renderer, + clipboard, + shell, + ) { + return event::Status::Captured; + } + + update( + self, + &event, + layout, + cursor_position, + shell, + tree.state.downcast_mut::(), + ) + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content.as_widget().mouse_interaction( + &tree.children[0], + layout.children().next().unwrap(), + cursor_position, + viewport, + renderer, + ) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Renderer::Theme, + renderer_style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + ) { + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + renderer_style, + layout.children().next().unwrap(), + cursor_position, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + self.content.as_widget_mut().overlay( + &mut tree.children[0], + layout.children().next().unwrap(), + renderer, + ) + } + + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Message: 'a + Clone, + Renderer: 'a + crate::Renderer, +{ + fn from( + listener: MouseListener<'a, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { + Element::new(listener) + } +} + +/// Processes the given [`Event`] and updates the [`State`] of an [`MouseListener`] +/// accordingly. +fn update( + widget: &mut MouseListener<'_, Message, Renderer>, + event: &Event, + layout: Layout<'_>, + cursor_position: Point, + shell: &mut Shell<'_, Message>, + state: &mut State, +) -> event::Status { + let hovered = state.hovered; + + if !layout.bounds().contains(cursor_position) { + if hovered { + state.hovered = false; + if let Some(message) = widget.on_mouse_exit.clone() { + shell.publish(message); + return event::Status::Captured; + } + } + + return event::Status::Ignored; + } + + state.hovered = true; + + if !hovered { + if let Some(message) = widget.on_mouse_enter.clone() { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_press.clone() { + if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_release.clone() { + if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_right_press.clone() { + if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = + event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_right_release.clone() { + if let Event::Mouse(mouse::Event::ButtonReleased( + mouse::Button::Right, + )) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_middle_press.clone() { + if let Event::Mouse(mouse::Event::ButtonPressed( + mouse::Button::Middle, + )) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_middle_release.clone() { + if let Event::Mouse(mouse::Event::ButtonReleased( + mouse::Button::Middle, + )) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + event::Status::Ignored +} + +/// Computes the layout of a [`MouseListener`]. +pub fn layout( + renderer: &Renderer, + limits: &layout::Limits, + width: Length, + height: Length, + max_height: u32, + max_width: u32, + layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, +) -> layout::Node { + let limits = limits + .loose() + .max_height(max_height) + .max_width(max_width) + .width(width) + .height(height); + + let content = layout_content(renderer, &limits); + let size = limits.resolve(content.size()); + + layout::Node::with_children(size, vec![content]) +} diff --git a/src/widget.rs b/src/widget.rs index c0ac716f17..15b34ffa4e 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -47,6 +47,14 @@ pub mod container { iced_native::widget::Container<'a, Message, Renderer>; } +pub mod mouse_listener { + //! Intercept mouse events on a widget. + + /// A container intercepting mouse events. + pub type MouseListener<'a, Message, Renderer = crate::Renderer> = + iced_native::widget::MouseListener<'a, Message, Renderer>; +} + pub mod pane_grid { //! Let your users split regions of your application and organize layout dynamically. //! From 28b0f7abf46ba6f6250e883104a6100e3e647172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:28:33 +0200 Subject: [PATCH 02/10] Delegate `layout` directly to `content` in `MouseListener` --- native/src/widget/mouse_listener.rs | 59 ++++++----------------------- 1 file changed, 11 insertions(+), 48 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index 0add7c7188..c4e91cba37 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -11,8 +11,6 @@ use crate::{ Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Widget, }; -use std::u32; - /// Emit messages on mouse events. #[allow(missing_debug_implementations)] pub struct MouseListener<'a, Message, Renderer> { @@ -151,17 +149,7 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout( - renderer, - limits, - Widget::::width(self), - Widget::::height(self), - u32::MAX, - u32::MAX, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) + self.content.as_widget().layout(renderer, limits) } fn operate( @@ -171,14 +159,12 @@ where renderer: &Renderer, operation: &mut dyn Operation, ) { - operation.container(None, &mut |operation| { - self.content.as_widget().operate( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - operation, - ); - }); + self.content.as_widget().operate( + &mut tree.children[0], + layout, + renderer, + operation, + ); } fn on_event( @@ -194,7 +180,7 @@ where if let event::Status::Captured = self.content.as_widget_mut().on_event( &mut tree.children[0], event.clone(), - layout.children().next().unwrap(), + layout, cursor_position, renderer, clipboard, @@ -223,7 +209,7 @@ where ) -> mouse::Interaction { self.content.as_widget().mouse_interaction( &tree.children[0], - layout.children().next().unwrap(), + layout, cursor_position, viewport, renderer, @@ -245,7 +231,7 @@ where renderer, theme, renderer_style, - layout.children().next().unwrap(), + layout, cursor_position, viewport, ); @@ -259,7 +245,7 @@ where ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], - layout.children().next().unwrap(), + layout, renderer, ) } @@ -378,26 +364,3 @@ fn update( event::Status::Ignored } - -/// Computes the layout of a [`MouseListener`]. -pub fn layout( - renderer: &Renderer, - limits: &layout::Limits, - width: Length, - height: Length, - max_height: u32, - max_width: u32, - layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, -) -> layout::Node { - let limits = limits - .loose() - .max_height(max_height) - .max_width(max_width) - .width(width) - .height(height); - - let content = layout_content(renderer, &limits); - let size = limits.resolve(content.size()); - - layout::Node::with_children(size, vec![content]) -} From d508ed26371edb3438ee4d1c6c9358713acd3bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:29:24 +0200 Subject: [PATCH 03/10] Remove redundant comments in `MouseListener` fields --- native/src/widget/mouse_listener.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index c4e91cba37..0ac7f82634 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -15,29 +15,13 @@ use crate::{ #[allow(missing_debug_implementations)] pub struct MouseListener<'a, Message, Renderer> { content: Element<'a, Message, Renderer>, - - /// Sets the message to emit on a left mouse button press. on_press: Option, - - /// Sets the message to emit on a left mouse button release. on_release: Option, - - /// Sets the message to emit on a right mouse button press. on_right_press: Option, - - /// Sets the message to emit on a right mouse button release. on_right_release: Option, - - /// Sets the message to emit on a middle mouse button press. on_middle_press: Option, - - /// Sets the message to emit on a middle mouse button release. on_middle_release: Option, - - /// Sets the message to emit when the mouse enters the widget. on_mouse_enter: Option, - - /// Sets the messsage to emit when the mouse exits the widget. on_mouse_exit: Option, } From 8bcb68d785cb1731894a00374970529cc59b0e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:30:09 +0200 Subject: [PATCH 04/10] Move `tag` and `state` definitions in `MouseListener` --- native/src/widget/mouse_listener.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index 0ac7f82634..3db2184e47 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -112,6 +112,14 @@ where Renderer: crate::Renderer, Message: Clone, { + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } + fn children(&self) -> Vec { vec![Tree::new(&self.content)] } @@ -233,14 +241,6 @@ where renderer, ) } - - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State::default()) - } } impl<'a, Message, Renderer> From> From f247528725982e3214df3b06c153347fe4945b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:34:17 +0200 Subject: [PATCH 05/10] Ignore mouse movements events in `MouseListener` These should be ignored generally, since they are considered passive user actions. --- native/src/widget/mouse_listener.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index 3db2184e47..f9c3acacc5 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -271,9 +271,9 @@ fn update( if !layout.bounds().contains(cursor_position) { if hovered { state.hovered = false; + if let Some(message) = widget.on_mouse_exit.clone() { shell.publish(message); - return event::Status::Captured; } } @@ -285,7 +285,8 @@ fn update( if !hovered { if let Some(message) = widget.on_mouse_enter.clone() { shell.publish(message); - return event::Status::Captured; + + return event::Status::Ignored; } } @@ -294,6 +295,7 @@ fn update( | Event::Touch(touch::Event::FingerPressed { .. }) = event { shell.publish(message); + return event::Status::Captured; } } @@ -303,6 +305,7 @@ fn update( | Event::Touch(touch::Event::FingerLifted { .. }) = event { shell.publish(message); + return event::Status::Captured; } } @@ -312,6 +315,7 @@ fn update( event { shell.publish(message); + return event::Status::Captured; } } @@ -322,6 +326,7 @@ fn update( )) = event { shell.publish(message); + return event::Status::Captured; } } @@ -332,6 +337,7 @@ fn update( )) = event { shell.publish(message); + return event::Status::Captured; } } @@ -342,6 +348,7 @@ fn update( )) = event { shell.publish(message); + return event::Status::Captured; } } From 29971c9d71fe81b12eae56eeaf87c6b6890c83f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:44:09 +0200 Subject: [PATCH 06/10] Avoid returning on mouse enter in `MouseListener` The event that triggers a mouse enter could be a mouse button press/release. --- native/src/widget/mouse_listener.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index f9c3acacc5..a7a06bcc31 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -86,7 +86,7 @@ impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { /// Local state of the [`MouseListener`]. #[derive(Default)] struct State { - hovered: bool, + is_hovered: bool, } impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { @@ -266,27 +266,23 @@ fn update( shell: &mut Shell<'_, Message>, state: &mut State, ) -> event::Status { - let hovered = state.hovered; + let was_hovered = state.is_hovered; - if !layout.bounds().contains(cursor_position) { - if hovered { - state.hovered = false; + state.is_hovered = layout.bounds().contains(cursor_position); - if let Some(message) = widget.on_mouse_exit.clone() { - shell.publish(message); + if !state.is_hovered { + if was_hovered { + if let Some(message) = widget.on_mouse_exit.as_ref() { + shell.publish(message.clone()); } } return event::Status::Ignored; } - state.hovered = true; - - if !hovered { - if let Some(message) = widget.on_mouse_enter.clone() { - shell.publish(message); - - return event::Status::Ignored; + if !was_hovered { + if let Some(message) = widget.on_mouse_enter.as_ref() { + shell.publish(message.clone()); } } From 6b359b496cf435c26f982ee0e52d7324c79ad1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:44:51 +0200 Subject: [PATCH 07/10] Avoid cloning messages unnecessarily in `MouseListener` --- native/src/widget/mouse_listener.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index a7a06bcc31..00e20c04fa 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -286,64 +286,64 @@ fn update( } } - if let Some(message) = widget.on_press.clone() { + if let Some(message) = widget.on_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_release.clone() { + if let Some(message) = widget.on_release.as_ref() { if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) | Event::Touch(touch::Event::FingerLifted { .. }) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_right_press.clone() { + if let Some(message) = widget.on_right_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_right_release.clone() { + if let Some(message) = widget.on_right_release.as_ref() { if let Event::Mouse(mouse::Event::ButtonReleased( mouse::Button::Right, )) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_middle_press.clone() { + if let Some(message) = widget.on_middle_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed( mouse::Button::Middle, )) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_middle_release.clone() { + if let Some(message) = widget.on_middle_release.as_ref() { if let Event::Mouse(mouse::Event::ButtonReleased( mouse::Button::Middle, )) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } From f55a97b738096d85086858026234ee95dcd79289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:46:33 +0200 Subject: [PATCH 08/10] Rename `MouseListener` to `MouseArea` --- native/src/widget.rs | 4 ++-- native/src/widget/helpers.rs | 6 ++--- .../{mouse_listener.rs => mouse_area.rs} | 24 +++++++++---------- src/widget.rs | 6 ++--- 4 files changed, 20 insertions(+), 20 deletions(-) rename native/src/widget/{mouse_listener.rs => mouse_area.rs} (93%) diff --git a/native/src/widget.rs b/native/src/widget.rs index 4eb3d1ba04..6849da1762 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -17,7 +17,7 @@ pub mod column; pub mod container; pub mod helpers; pub mod image; -pub mod mouse_listener; +pub mod mouse_area; pub mod operation; pub mod pane_grid; pub mod pick_list; @@ -52,7 +52,7 @@ pub use helpers::*; #[doc(no_inline)] pub use image::Image; #[doc(no_inline)] -pub use mouse_listener::MouseListener; +pub use mouse_area::MouseArea; #[doc(no_inline)] pub use pane_grid::PaneGrid; #[doc(no_inline)] diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index 88986d6086..5f44e22c1e 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -316,11 +316,11 @@ where } /// A container intercepting mouse events. -pub fn mouse_listener<'a, Message, Renderer>( +pub fn mouse_area<'a, Message, Renderer>( widget: impl Into>, -) -> widget::MouseListener<'a, Message, Renderer> +) -> widget::MouseArea<'a, Message, Renderer> where Renderer: crate::Renderer, { - widget::MouseListener::new(widget) + widget::MouseArea::new(widget) } diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_area.rs similarity index 93% rename from native/src/widget/mouse_listener.rs rename to native/src/widget/mouse_area.rs index 00e20c04fa..1b8b318c62 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_area.rs @@ -13,7 +13,7 @@ use crate::{ /// Emit messages on mouse events. #[allow(missing_debug_implementations)] -pub struct MouseListener<'a, Message, Renderer> { +pub struct MouseArea<'a, Message, Renderer> { content: Element<'a, Message, Renderer>, on_press: Option, on_release: Option, @@ -25,7 +25,7 @@ pub struct MouseListener<'a, Message, Renderer> { on_mouse_exit: Option, } -impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { +impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { /// The message to emit on a left button press. #[must_use] pub fn on_press(mut self, message: Message) -> Self { @@ -83,16 +83,16 @@ impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { } } -/// Local state of the [`MouseListener`]. +/// Local state of the [`MouseArea`]. #[derive(Default)] struct State { is_hovered: bool, } -impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { - /// Creates an empty [`MouseListener`]. +impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { + /// Creates an empty [`MouseArea`]. pub fn new(content: impl Into>) -> Self { - MouseListener { + MouseArea { content: content.into(), on_press: None, on_release: None, @@ -107,7 +107,7 @@ impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { } impl<'a, Message, Renderer> Widget - for MouseListener<'a, Message, Renderer> + for MouseArea<'a, Message, Renderer> where Renderer: crate::Renderer, Message: Clone, @@ -243,23 +243,23 @@ where } } -impl<'a, Message, Renderer> From> +impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, Renderer: 'a + crate::Renderer, { fn from( - listener: MouseListener<'a, Message, Renderer>, + area: MouseArea<'a, Message, Renderer>, ) -> Element<'a, Message, Renderer> { - Element::new(listener) + Element::new(area) } } -/// Processes the given [`Event`] and updates the [`State`] of an [`MouseListener`] +/// Processes the given [`Event`] and updates the [`State`] of an [`MouseArea`] /// accordingly. fn update( - widget: &mut MouseListener<'_, Message, Renderer>, + widget: &mut MouseArea<'_, Message, Renderer>, event: &Event, layout: Layout<'_>, cursor_position: Point, diff --git a/src/widget.rs b/src/widget.rs index 15b34ffa4e..d06fdafb24 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -47,12 +47,12 @@ pub mod container { iced_native::widget::Container<'a, Message, Renderer>; } -pub mod mouse_listener { +pub mod mouse_area { //! Intercept mouse events on a widget. /// A container intercepting mouse events. - pub type MouseListener<'a, Message, Renderer = crate::Renderer> = - iced_native::widget::MouseListener<'a, Message, Renderer>; + pub type MouseArea<'a, Message, Renderer = crate::Renderer> = + iced_native::widget::MouseArea<'a, Message, Renderer>; } pub mod pane_grid { From 020f1120e332b450f12ccf7aa3f4a52c5ee282a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:50:00 +0200 Subject: [PATCH 09/10] Fix documentation of `MouseArea::new` --- native/src/widget/mouse_area.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/widget/mouse_area.rs b/native/src/widget/mouse_area.rs index 1b8b318c62..5bf096d306 100644 --- a/native/src/widget/mouse_area.rs +++ b/native/src/widget/mouse_area.rs @@ -90,7 +90,7 @@ struct State { } impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { - /// Creates an empty [`MouseArea`]. + /// Creates a [`MouseArea`] with the given content. pub fn new(content: impl Into>) -> Self { MouseArea { content: content.into(), From 0c39112a2e7c63e7f8c2a03e67216b3736cb3aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:58:53 +0200 Subject: [PATCH 10/10] Remove support for `on_mouse_enter` and `on_mouse_exit` in `MouseArea` These need continuity guarantees (e.g. mandatory widget id), which we don't have yet! --- native/src/widget/mouse_area.rs | 48 +++------------------------------ 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/native/src/widget/mouse_area.rs b/native/src/widget/mouse_area.rs index 5bf096d306..69cfddbfde 100644 --- a/native/src/widget/mouse_area.rs +++ b/native/src/widget/mouse_area.rs @@ -21,8 +21,6 @@ pub struct MouseArea<'a, Message, Renderer> { on_right_release: Option, on_middle_press: Option, on_middle_release: Option, - on_mouse_enter: Option, - on_mouse_exit: Option, } impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { @@ -67,26 +65,12 @@ impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { self.on_middle_release = Some(message); self } - - /// The message to emit when the mouse enters the widget. - #[must_use] - pub fn on_mouse_enter(mut self, message: Message) -> Self { - self.on_mouse_enter = Some(message); - self - } - - /// The messsage to emit when the mouse exits the widget. - #[must_use] - pub fn on_mouse_exit(mut self, message: Message) -> Self { - self.on_mouse_exit = Some(message); - self - } } /// Local state of the [`MouseArea`]. #[derive(Default)] struct State { - is_hovered: bool, + // TODO: Support on_mouse_enter and on_mouse_exit } impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { @@ -100,8 +84,6 @@ impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { on_right_release: None, on_middle_press: None, on_middle_release: None, - on_mouse_enter: None, - on_mouse_exit: None, } } } @@ -181,14 +163,7 @@ where return event::Status::Captured; } - update( - self, - &event, - layout, - cursor_position, - shell, - tree.state.downcast_mut::(), - ) + update(self, &event, layout, cursor_position, shell) } fn mouse_interaction( @@ -264,28 +239,11 @@ fn update( layout: Layout<'_>, cursor_position: Point, shell: &mut Shell<'_, Message>, - state: &mut State, ) -> event::Status { - let was_hovered = state.is_hovered; - - state.is_hovered = layout.bounds().contains(cursor_position); - - if !state.is_hovered { - if was_hovered { - if let Some(message) = widget.on_mouse_exit.as_ref() { - shell.publish(message.clone()); - } - } - + if !layout.bounds().contains(cursor_position) { return event::Status::Ignored; } - if !was_hovered { - if let Some(message) = widget.on_mouse_enter.as_ref() { - shell.publish(message.clone()); - } - } - if let Some(message) = widget.on_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) = event