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();