mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Fix Sense::drag detecting drags when clicking widget above it (#8396)
Co-authored-by: Lucas Meurer <lucas@rerun.io> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -198,17 +198,22 @@ pub(crate) fn interact(
|
|||||||
// When the mouse first is pressed, it could be either,
|
// When the mouse first is pressed, it could be either,
|
||||||
// so we postpone the decision until we know.
|
// so we postpone the decision until we know.
|
||||||
//
|
//
|
||||||
// …unless a click is no longer possible at all: a click has to be
|
// …unless the pointer has left the widget: a click has to be
|
||||||
// released on the widget, and `hits.click` tells us whether the
|
// released on the widget, so once the pointer is outside there is
|
||||||
// pointer is still somewhere a release would land on this widget.
|
// nothing left to wait for.
|
||||||
// Note that this is not the same as being inside `interact_rect`:
|
|
||||||
// the hit-test also picks up widgets within `interact_radius`, and
|
|
||||||
// lets a widget on top take the hit.
|
|
||||||
//
|
//
|
||||||
// Deciding here means a thin drag handle (narrower than
|
// Deciding here means a thin drag handle (narrower than
|
||||||
// `max_click_dist`) doesn't spend the decision window as neither
|
// `max_click_dist`) doesn't spend the decision window as neither
|
||||||
// hovered nor dragged, which would make its highlight blink out.
|
// hovered nor dragged, which would make its highlight blink out.
|
||||||
let could_still_be_clicked = hits.click.is_some_and(|hit| hit.id == widget.id);
|
// The hit-test picks up widgets within `interact_radius`, so
|
||||||
|
// `hits.click` can name such a handle even when the pointer is a
|
||||||
|
// few points outside it.
|
||||||
|
//
|
||||||
|
// A widget on top might "steal" the click hit, but then the pointer is still inside
|
||||||
|
// us, and pressing that button must not start a drag. So we check both.
|
||||||
|
let pointer_is_inside = hits.contains_pointer.iter().any(|w| w.id == widget.id);
|
||||||
|
let could_still_be_clicked =
|
||||||
|
pointer_is_inside || hits.click.is_some_and(|hit| hit.id == widget.id);
|
||||||
input.pointer.is_decidedly_dragging() || !could_still_be_clicked
|
input.pointer.is_decidedly_dragging() || !could_still_be_clicked
|
||||||
} else {
|
} else {
|
||||||
// This widget is just sensitive to drags, so we can mark it as dragged right away:
|
// This widget is just sensitive to drags, so we can mark it as dragged right away:
|
||||||
|
|||||||
@@ -185,3 +185,50 @@ fn click_inside_a_widget_still_clicks() {
|
|||||||
"press and release without moving should be a click"
|
"press and release without moving should be a click"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A button inside a draggable row takes the click hit, because it is on top.
|
||||||
|
/// The pointer is still inside the row though, so the press must stay undecided —
|
||||||
|
/// otherwise the row starts dragging the moment the user touches the button.
|
||||||
|
#[test]
|
||||||
|
fn press_on_a_button_inside_a_draggable_row_stays_undecided() {
|
||||||
|
let button_id = Id::new("button");
|
||||||
|
let button_size = Vec2::new(50.0, 20.0);
|
||||||
|
|
||||||
|
let mut harness = Harness::builder()
|
||||||
|
.with_step_dt(1.0 / 60.0)
|
||||||
|
.with_size(Vec2::new(300.0, 200.0))
|
||||||
|
.build_ui(move |ui| {
|
||||||
|
let row_rect = ui.max_rect();
|
||||||
|
ui.interact(row_rect, widget_id(), Sense::click_and_drag());
|
||||||
|
|
||||||
|
// Allocated after the row, so it ends up _on top_ of it.
|
||||||
|
let button_rect = Rect::from_min_size(row_rect.min, button_size);
|
||||||
|
ui.interact(button_rect, button_id, Sense::click());
|
||||||
|
});
|
||||||
|
harness.step();
|
||||||
|
|
||||||
|
let grab = Rect::from_min_size(widget_rect(&harness).min, button_size).center();
|
||||||
|
press_at(&mut harness, grab);
|
||||||
|
|
||||||
|
let (_hovered, dragged) = widget_state(&harness);
|
||||||
|
assert!(
|
||||||
|
!dragged,
|
||||||
|
"pressing a button inside the row must not start dragging the row"
|
||||||
|
);
|
||||||
|
|
||||||
|
harness.event(egui::Event::PointerButton {
|
||||||
|
pos: grab,
|
||||||
|
button: egui::PointerButton::Primary,
|
||||||
|
pressed: false,
|
||||||
|
modifiers: egui::Modifiers::NONE,
|
||||||
|
});
|
||||||
|
harness.step();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
harness
|
||||||
|
.ctx
|
||||||
|
.read_response(button_id)
|
||||||
|
.is_some_and(|r| r.clicked()),
|
||||||
|
"the button should have been clicked"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user