Fix PaneGrid mouse interaction when clicking a pane

This commit is contained in:
Héctor Ramón Jiménez 2024-02-27 02:19:41 +01:00
parent 8bb1587a74
commit c723c101d4
No known key found for this signature in database
GPG key ID: 0BF4EC06CD8E5686
2 changed files with 60 additions and 100 deletions

View file

@ -46,6 +46,9 @@ use crate::core::{
Vector, Widget, 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 /// A collection of panes distributed using either vertical or horizontal splits
/// to completely fill the space available. /// to completely fill the space available.
/// ///
@ -532,8 +535,6 @@ pub fn update<'a, Message, T: Draggable>(
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
on_resize: &Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>, on_resize: &Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>,
) -> event::Status { ) -> event::Status {
const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
let mut event_status = event::Status::Ignored; let mut event_status = event::Status::Ignored;
match event { match event {
@ -575,6 +576,7 @@ pub fn update<'a, Message, T: Draggable>(
shell, shell,
contents, contents,
on_click, on_click,
on_drag,
); );
} }
} }
@ -586,6 +588,7 @@ pub fn update<'a, Message, T: Draggable>(
shell, shell,
contents, contents,
on_click, on_click,
on_drag,
); );
} }
} }
@ -594,38 +597,44 @@ pub fn update<'a, Message, T: Draggable>(
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
| Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLifted { .. })
| Event::Touch(touch::Event::FingerLost { .. }) => { | 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(on_drag) = on_drag {
if let Some(cursor_position) = cursor.position() { if let Some(cursor_position) = cursor.position() {
let event = if let Some(edge) = if cursor_position.distance(origin)
in_edge(layout, cursor_position) > DRAG_DEADBAND_DISTANCE
{ {
DragEvent::Dropped { let event = if let Some(edge) =
pane, in_edge(layout, cursor_position)
target: Target::Edge(edge), {
} DragEvent::Dropped {
} else { pane,
let dropped_region = contents target: Target::Edge(edge),
.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 }, } else {
} let dropped_region = contents
}; .zip(layout.children())
.find_map(|(target, layout)| {
layout_region(layout, cursor_position)
.map(|region| (target, region))
});
shell.publish(on_drag(event)); match dropped_region {
Some(((target, _), region))
if pane != target =>
{
DragEvent::Dropped {
pane,
target: Target::Pane(
target, region,
),
}
}
_ => DragEvent::Canceled { pane },
}
};
shell.publish(on_drag(event));
}
} }
} }
@ -638,49 +647,7 @@ pub fn update<'a, Message, T: Draggable>(
} }
Event::Mouse(mouse::Event::CursorMoved { .. }) Event::Mouse(mouse::Event::CursorMoved { .. })
| Event::Touch(touch::Event::FingerMoved { .. }) => { | Event::Touch(touch::Event::FingerMoved { .. }) => {
if let Some((_, origin)) = action.clicked_pane() { if let Some((_, on_resize)) = on_resize {
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((split, _)) = action.picked_split() { if let Some((split, _)) = action.picked_split() {
let bounds = layout.bounds(); let bounds = layout.bounds();
@ -755,6 +722,7 @@ fn click_pane<'a, Message, T>(
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
contents: impl Iterator<Item = (Pane, T)>, contents: impl Iterator<Item = (Pane, T)>,
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>, on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
) where ) where
T: Draggable, T: Draggable,
{ {
@ -762,15 +730,21 @@ fn click_pane<'a, Message, T>(
.zip(layout.children()) .zip(layout.children())
.filter(|(_, layout)| layout.bounds().contains(cursor_position)); .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 { if let Some(on_click) = &on_click {
shell.publish(on_click(pane)); shell.publish(on_click(pane));
} }
let pane_position = layout.position(); if let Some(on_drag) = &on_drag {
let origin = if content.can_be_dragged_at(layout, cursor_position) {
cursor_position - Vector::new(pane_position.x, pane_position.y); *action = state::Action::Dragging {
*action = state::Action::Clicking { pane, origin }; pane,
origin: cursor_position,
};
shell.publish(on_drag(DragEvent::Picked { pane }));
}
}
} }
} }
@ -783,7 +757,7 @@ pub fn mouse_interaction(
spacing: f32, spacing: f32,
resize_leeway: Option<f32>, resize_leeway: Option<f32>,
) -> Option<mouse::Interaction> { ) -> Option<mouse::Interaction> {
if action.clicked_pane().is_some() || action.picked_pane().is_some() { if action.picked_pane().is_some() {
return Some(mouse::Interaction::Grabbing); return Some(mouse::Interaction::Grabbing);
} }
@ -841,7 +815,13 @@ pub fn draw<Theme, Renderer, T>(
Theme: StyleSheet, Theme: StyleSheet,
Renderer: crate::core::Renderer, 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 let picked_split = action
.picked_split() .picked_split()
@ -962,8 +942,7 @@ pub fn draw<Theme, Renderer, T>(
if let Some(cursor_position) = cursor.position() { if let Some(cursor_position) = cursor.position() {
let bounds = layout.bounds(); let bounds = layout.bounds();
let translation = cursor_position let translation = cursor_position - Point::new(origin.x, origin.y);
- Point::new(bounds.x + origin.x, bounds.y + origin.y);
renderer.with_translation(translation, |renderer| { renderer.with_translation(translation, |renderer| {
renderer.with_layer(bounds, |renderer| { renderer.with_layer(bounds, |renderer| {
@ -1020,8 +999,6 @@ pub fn draw<Theme, Renderer, T>(
} }
} }
const THICKNESS_RATIO: f32 = 25.0;
fn in_edge(layout: Layout<'_>, cursor: Point) -> Option<Edge> { fn in_edge(layout: Layout<'_>, cursor: Point) -> Option<Edge> {
let bounds = layout.bounds(); let bounds = layout.bounds();

View file

@ -403,15 +403,6 @@ pub enum Action {
/// ///
/// [`PaneGrid`]: super::PaneGrid /// [`PaneGrid`]: super::PaneGrid
Idle, 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. /// A [`Pane`] in the [`PaneGrid`] is being dragged.
/// ///
/// [`PaneGrid`]: super::PaneGrid /// [`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. /// Returns the current [`Split`] that is being dragged, if any.
pub fn picked_split(&self) -> Option<(Split, Axis)> { pub fn picked_split(&self) -> Option<(Split, Axis)> {
match *self { match *self {