From c723c101d4e5be45ad6dbe49d92cd7980b954a58 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 27 Feb 2024 02:19:41 +0100 Subject: Fix `PaneGrid` mouse interaction when clicking a pane --- widget/src/pane_grid.rs | 141 ++++++++++++++++++------------------------ widget/src/pane_grid/state.rs | 17 ----- 2 files changed, 59 insertions(+), 99 deletions(-) diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 37562504..478a7024 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -46,6 +46,9 @@ use crate::core::{ Vector, Widget, }; +const DRAG_DEADBAND_DISTANCE: f32 = 10.0; +const THICKNESS_RATIO: f32 = 25.0; + /// A collection of panes distributed using either vertical or horizontal splits /// to completely fill the space available. /// @@ -532,8 +535,6 @@ pub fn update<'a, Message, T: Draggable>( on_drag: &Option Message + 'a>>, on_resize: &Option<(f32, Box Message + 'a>)>, ) -> event::Status { - const DRAG_DEADBAND_DISTANCE: f32 = 10.0; - let mut event_status = event::Status::Ignored; match event { @@ -575,6 +576,7 @@ pub fn update<'a, Message, T: Draggable>( shell, contents, on_click, + on_drag, ); } } @@ -586,6 +588,7 @@ pub fn update<'a, Message, T: Draggable>( shell, contents, on_click, + on_drag, ); } } @@ -594,38 +597,44 @@ pub fn update<'a, Message, T: Draggable>( Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) | Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLost { .. }) => { - if let Some((pane, _)) = action.picked_pane() { + if let Some((pane, origin)) = action.picked_pane() { if let Some(on_drag) = on_drag { if let Some(cursor_position) = cursor.position() { - let event = if let Some(edge) = - in_edge(layout, cursor_position) + if cursor_position.distance(origin) + > DRAG_DEADBAND_DISTANCE { - DragEvent::Dropped { - pane, - target: Target::Edge(edge), - } - } else { - let dropped_region = contents - .zip(layout.children()) - .find_map(|(target, layout)| { - layout_region(layout, cursor_position) - .map(|region| (target, region)) - }); - - match dropped_region { - Some(((target, _), region)) - if pane != target => - { - DragEvent::Dropped { - pane, - target: Target::Pane(target, region), + let event = if let Some(edge) = + in_edge(layout, cursor_position) + { + DragEvent::Dropped { + pane, + target: Target::Edge(edge), + } + } else { + let dropped_region = contents + .zip(layout.children()) + .find_map(|(target, layout)| { + layout_region(layout, cursor_position) + .map(|region| (target, region)) + }); + + match dropped_region { + Some(((target, _), region)) + if pane != target => + { + DragEvent::Dropped { + pane, + target: Target::Pane( + target, region, + ), + } } + _ => DragEvent::Canceled { pane }, } - _ => DragEvent::Canceled { pane }, - } - }; + }; - shell.publish(on_drag(event)); + shell.publish(on_drag(event)); + } } } @@ -638,49 +647,7 @@ pub fn update<'a, Message, T: Draggable>( } Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { - if let Some((_, origin)) = action.clicked_pane() { - if let Some(on_drag) = &on_drag { - let bounds = layout.bounds(); - - if let Some(cursor_position) = cursor.position_over(bounds) - { - let mut clicked_region = contents - .zip(layout.children()) - .filter(|(_, layout)| { - layout.bounds().contains(cursor_position) - }); - - if let Some(((pane, content), layout)) = - clicked_region.next() - { - if content - .can_be_dragged_at(layout, cursor_position) - { - let pane_position = layout.position(); - - let new_origin = cursor_position - - Vector::new( - pane_position.x, - pane_position.y, - ); - - if new_origin.distance(origin) - > DRAG_DEADBAND_DISTANCE - { - *action = state::Action::Dragging { - pane, - origin, - }; - - shell.publish(on_drag(DragEvent::Picked { - pane, - })); - } - } - } - } - } - } else if let Some((_, on_resize)) = on_resize { + if let Some((_, on_resize)) = on_resize { if let Some((split, _)) = action.picked_split() { let bounds = layout.bounds(); @@ -755,6 +722,7 @@ fn click_pane<'a, Message, T>( shell: &mut Shell<'_, Message>, contents: impl Iterator, on_click: &Option Message + 'a>>, + on_drag: &Option Message + 'a>>, ) where T: Draggable, { @@ -762,15 +730,21 @@ fn click_pane<'a, Message, T>( .zip(layout.children()) .filter(|(_, layout)| layout.bounds().contains(cursor_position)); - if let Some(((pane, _), layout)) = clicked_region.next() { + if let Some(((pane, content), layout)) = clicked_region.next() { if let Some(on_click) = &on_click { shell.publish(on_click(pane)); } - let pane_position = layout.position(); - let origin = - cursor_position - Vector::new(pane_position.x, pane_position.y); - *action = state::Action::Clicking { pane, origin }; + if let Some(on_drag) = &on_drag { + if content.can_be_dragged_at(layout, cursor_position) { + *action = state::Action::Dragging { + pane, + origin: cursor_position, + }; + + shell.publish(on_drag(DragEvent::Picked { pane })); + } + } } } @@ -783,7 +757,7 @@ pub fn mouse_interaction( spacing: f32, resize_leeway: Option, ) -> Option { - if action.clicked_pane().is_some() || action.picked_pane().is_some() { + if action.picked_pane().is_some() { return Some(mouse::Interaction::Grabbing); } @@ -841,7 +815,13 @@ pub fn draw( Theme: StyleSheet, Renderer: crate::core::Renderer, { - let picked_pane = action.picked_pane(); + let picked_pane = action.picked_pane().filter(|(_, origin)| { + cursor + .position() + .map(|position| position.distance(*origin)) + .unwrap_or_default() + > DRAG_DEADBAND_DISTANCE + }); let picked_split = action .picked_split() @@ -962,8 +942,7 @@ pub fn draw( if let Some(cursor_position) = cursor.position() { let bounds = layout.bounds(); - let translation = cursor_position - - Point::new(bounds.x + origin.x, bounds.y + origin.y); + let translation = cursor_position - Point::new(origin.x, origin.y); renderer.with_translation(translation, |renderer| { renderer.with_layer(bounds, |renderer| { @@ -1020,8 +999,6 @@ pub fn draw( } } -const THICKNESS_RATIO: f32 = 25.0; - fn in_edge(layout: Layout<'_>, cursor: Point) -> Option { let bounds = layout.bounds(); diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index 5d1fe254..481cd770 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -403,15 +403,6 @@ pub enum Action { /// /// [`PaneGrid`]: super::PaneGrid Idle, - /// A [`Pane`] in the [`PaneGrid`] is being clicked. - /// - /// [`PaneGrid`]: super::PaneGrid - Clicking { - /// The [`Pane`] being clicked. - pane: Pane, - /// The starting [`Point`] of the click interaction. - origin: Point, - }, /// A [`Pane`] in the [`PaneGrid`] is being dragged. /// /// [`PaneGrid`]: super::PaneGrid @@ -441,14 +432,6 @@ impl Action { } } - /// Returns the current [`Pane`] that is being clicked, if any. - pub fn clicked_pane(&self) -> Option<(Pane, Point)> { - match *self { - Action::Clicking { pane, origin, .. } => Some((pane, origin)), - _ => None, - } - } - /// Returns the current [`Split`] that is being dragged, if any. pub fn picked_split(&self) -> Option<(Split, Axis)> { match *self { -- cgit