From f55a97b738096d85086858026234ee95dcd79289 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 13 Apr 2023 06:46:33 +0200 Subject: Rename `MouseListener` to `MouseArea` --- native/src/widget/mouse_area.rs | 353 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 native/src/widget/mouse_area.rs (limited to 'native/src/widget/mouse_area.rs') diff --git a/native/src/widget/mouse_area.rs b/native/src/widget/mouse_area.rs new file mode 100644 index 00000000..1b8b318c --- /dev/null +++ b/native/src/widget/mouse_area.rs @@ -0,0 +1,353 @@ +//! 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, +}; + +/// Emit messages on mouse events. +#[allow(missing_debug_implementations)] +pub struct MouseArea<'a, Message, Renderer> { + content: Element<'a, Message, Renderer>, + on_press: Option, + on_release: Option, + on_right_press: Option, + 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> { + /// 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 [`MouseArea`]. +#[derive(Default)] +struct State { + is_hovered: bool, +} + +impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { + /// Creates an empty [`MouseArea`]. + pub fn new(content: impl Into>) -> Self { + MouseArea { + 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 MouseArea<'a, Message, Renderer> +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)] + } + + 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 { + self.content.as_widget().layout(renderer, limits) + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + self.content.as_widget().operate( + &mut tree.children[0], + layout, + 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, + 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, + 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, + 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, + renderer, + ) + } +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Message: 'a + Clone, + Renderer: 'a + crate::Renderer, +{ + fn from( + area: MouseArea<'a, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { + Element::new(area) + } +} + +/// Processes the given [`Event`] and updates the [`State`] of an [`MouseArea`] +/// accordingly. +fn update( + widget: &mut MouseArea<'_, Message, Renderer>, + event: &Event, + 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()); + } + } + + 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 + { + shell.publish(message.clone()); + + return event::Status::Captured; + } + } + + 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.clone()); + + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_right_press.as_ref() { + if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = + event + { + shell.publish(message.clone()); + + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_right_release.as_ref() { + if let Event::Mouse(mouse::Event::ButtonReleased( + mouse::Button::Right, + )) = event + { + shell.publish(message.clone()); + + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_middle_press.as_ref() { + if let Event::Mouse(mouse::Event::ButtonPressed( + mouse::Button::Middle, + )) = event + { + shell.publish(message.clone()); + + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_middle_release.as_ref() { + if let Event::Mouse(mouse::Event::ButtonReleased( + mouse::Button::Middle, + )) = event + { + shell.publish(message.clone()); + + return event::Status::Captured; + } + } + + event::Status::Ignored +} -- cgit From 020f1120e332b450f12ccf7aa3f4a52c5ee282a8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 13 Apr 2023 06:50:00 +0200 Subject: Fix documentation of `MouseArea::new` --- native/src/widget/mouse_area.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'native/src/widget/mouse_area.rs') diff --git a/native/src/widget/mouse_area.rs b/native/src/widget/mouse_area.rs index 1b8b318c..5bf096d3 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(), -- cgit From 0c39112a2e7c63e7f8c2a03e67216b3736cb3aeb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 13 Apr 2023 06:58:53 +0200 Subject: 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(-) (limited to 'native/src/widget/mouse_area.rs') diff --git a/native/src/widget/mouse_area.rs b/native/src/widget/mouse_area.rs index 5bf096d3..69cfddbf 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 -- cgit