From fa75b6f53dfa329af6d04882b8b2798b153e8060 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 27 Oct 2026 22:11:10 -0700 Subject: [PATCH] Part of #4326: a lost mouse-up after the pointer moved does not click at the press The released buttons go after the frame's last pointer movement, at its position. Before, they went first, at the press position, so a press that moved past the click distance in the same frame ended as a click on the widget under the press. --- crates/ui-egui/src/app/input.rs | 12 +++++++++++- crates/ui-egui/src/panel_docking/tests.rs | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/app/input.rs b/crates/ui-egui/src/app/input.rs index 9fd967ccd..986ec60ae 100644 --- a/crates/ui-egui/src/app/input.rs +++ b/crates/ui-egui/src/app/input.rs @@ -151,9 +151,19 @@ impl VectorcraftApp { .collect(); (down, p.latest_pos().or_else(|| p.interact_pos()).or_else(|| p.press_origin()), i.modifiers) }); + // The releases go after this frame's last pointer movement, at its position, so a pointer + // that moved away from the press does not end it with a click at the press position. + let moved = raw.events.iter().enumerate().rev().find_map(|(i, e)| match e { + egui::Event::PointerMoved(p) => Some((i + 1, *p)), + _ => None, + }); + let (at, pos) = match moved { + Some((at, p)) => (at, Some(p)), + None => (0, pos), + }; let Some(pos) = pos else { return }; let releases = down.into_iter().map(|button| egui::Event::PointerButton { pos, button, pressed: false, modifiers }); - raw.events.splice(0..0, releases); + raw.events.splice(at..at, releases); } /// Inject synthetic events (one press/release step, wheel turn or short key sequence per host diff --git a/crates/ui-egui/src/panel_docking/tests.rs b/crates/ui-egui/src/panel_docking/tests.rs index eb0b10bd3..097da2999 100644 --- a/crates/ui-egui/src/panel_docking/tests.rs +++ b/crates/ui-egui/src/panel_docking/tests.rs @@ -439,6 +439,15 @@ fn a_column_resize_continues_while_the_system_reports_the_button_down() { assert!(h.ctx.input(|i| i.pointer.primary_down())); } +#[test] +fn a_lost_release_after_the_pointer_moved_away_is_not_a_click() { + let mut h = Harness::new(); + h.frame(vec![Event::PointerMoved(pos2(800.0, 600.0)), button(pos2(800.0, 600.0), true)]); + h.frame_with_system_buttons(vec![Event::PointerMoved(pos2(1000.0, 600.0))], false); + assert!(!h.ctx.input(|i| i.pointer.any_down())); + assert!(!h.ctx.input(|i| i.pointer.any_click()), "the release at the press position made a click"); +} + #[test] fn control_channel_drags_keep_their_button_between_steps() { let mut h = Harness::new();