From 9e305d642e3e091235e8a651079f6dafd798c224 Mon Sep 17 00:00:00 2001 From: helgehelge123 <117832631+helgehelge123@users.noreply.github.com> Date: Sun, 18 Oct 2026 09:39:32 +0200 Subject: [PATCH] KEY-TOGGLEZOOM: a click down and up within one frame keeps its zoom on Space's release A real mouse can deliver the press and the release in the same egui frame: no button is down then, so the hand was not marked used and a quick release of Space ran the tap fallback, zooming straight back. tick() now also counts a press seen this frame. New headless test sends both button events in one frame (the synthetic pointer queue sends one per frame, which is why the existing tests missed it). Reported by AlexChambers in review. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/space_hand.rs | 5 +++-- crates/ui-egui/src/tests_space_hand.rs | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/space_hand.rs b/crates/ui-egui/src/space_hand.rs index bd905e183..a02f2ac84 100644 --- a/crates/ui-egui/src/space_hand.rs +++ b/crates/ui-egui/src/space_hand.rs @@ -53,8 +53,9 @@ pub(crate) fn begin(app: &mut LightcraftApp, ctx: &egui::Context) -> bool { /// zoom as it ends. pub(crate) fn tick(app: &mut LightcraftApp, ctx: &egui::Context) { let Some(mut hand) = app.space_hand else { return }; - let (held, pointer, now) = ctx.input(|i| (i.key_down(Key::Space), i.pointer.any_down(), i.time)); - hand.used |= pointer; + let (held, pointer, pressed, now) = ctx.input(|i| (i.key_down(Key::Space), i.pointer.any_down(), i.pointer.any_pressed(), i.time)); + // a quick click can go down and up within one frame: no button is down then, but it pressed + hand.used |= pointer || pressed; let tool = tool_owns_pointer(app) && app.ui.dialog.is_none(); // held, or let go in the middle of a pan: the hand stays until the button is up if tool && (held || pointer) { diff --git a/crates/ui-egui/src/tests_space_hand.rs b/crates/ui-egui/src/tests_space_hand.rs index 8bf3ba992..701408e34 100644 --- a/crates/ui-egui/src/tests_space_hand.rs +++ b/crates/ui-egui/src/tests_space_hand.rs @@ -158,6 +158,26 @@ fn space_held_click_zooms_to_the_point_clicked_and_back() { assert_eq!(develop(&h).spots.len(), spots, "a click painted a spot"); } +#[test] +fn a_click_down_and_up_within_one_frame_is_not_a_tap() { + // a real mouse can deliver the press and the release in the same frame: no button is down + // then, yet the click zoomed, and letting go of Space quickly must not zoom back + let mut h = detail(Some("panel.remove")); + space(&mut h, "down"); + pointer(&mut h, json!([{"kind": "move", "x": 0.25, "y": 0.3}])); + let img = h.app.image_rect.unwrap(); + let pos = egui::pos2(img.left() + 0.25 * img.width(), img.top() + 0.3 * img.height()); + for pressed in [true, false] { + h.events.push(egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: egui::Modifiers::NONE }); + } + h.step(); + let zoomed = Zoom::Percent(h.app.ui.click_zoom as f32); + assert_eq!(h.app.ui.zoom, zoomed, "the click did not zoom"); + space(&mut h, "up"); + assert!(h.app.space_hand.is_none()); + assert_eq!(h.app.ui.zoom, zoomed, "the release toggled the zoom back"); +} + #[test] fn a_tap_over_a_tool_toggles_on_release_and_a_long_hold_does_not() { let mut h = detail(Some("panel.remove"));