diff --git a/crates/ui-egui/src/canvas/page_pointer.rs b/crates/ui-egui/src/canvas/page_pointer.rs index 02cb2208ed..f56614411d 100644 --- a/crates/ui-egui/src/canvas/page_pointer.rs +++ b/crates/ui-egui/src/canvas/page_pointer.rs @@ -78,6 +78,15 @@ impl PagePointer { pub(crate) fn pressed(&self) -> Option { first_press(&self.edges) } + + /// Where this frame's last primary release was: where a click came up, which the pointer may + /// have left by the frame's end. + pub(crate) fn released(&self) -> Option { + self.edges.iter().rev().find_map(|e| match e { + Edge::Release(p) => Some(*p), + Edge::Press(_) => None, + }) + } } /// Whether egui gives the drag to a widget that is not the pages' (a panel's tab, a floating panel's diff --git a/crates/ui-egui/src/tools/camera_ui.rs b/crates/ui-egui/src/tools/camera_ui.rs index f2e07fab35..99b4356a26 100644 --- a/crates/ui-egui/src/tools/camera_ui.rs +++ b/crates/ui-egui/src/tools/camera_ui.rs @@ -99,8 +99,9 @@ pub(crate) fn page_input(resp: &egui::Response, xf: &PageXform, ptr: &PagePointe view.camera = Some(CameraQuad::inset(page, xf.pw, xf.ph)); } // A press nobody holds on a page without the quadrilateral moves it there: a drag from where it - // began, a click where it came up (egui has cleared the press's origin by its release). - let at = if resp.clicked() { pointer } else { origin }; + // began, a click where it came up (egui has cleared the press's origin by its release, and the + // pointer may have moved on in the same frame). + let at = if resp.clicked() { ptr.released() } else { origin }; if (resp.drag_started() || resp.clicked()) && press == PagePress::Free && at.is_some_and(|p| xf.rect.contains(p)) diff --git a/crates/ui-egui/tests/it/pointer.rs b/crates/ui-egui/tests/it/pointer.rs index 2fc79d1f63..d01a74cff0 100644 --- a/crates/ui-egui/tests/it/pointer.rs +++ b/crates/ui-egui/tests/it/pointer.rs @@ -1739,3 +1739,22 @@ fn a_camera_click_on_another_page_moves_the_quadrilateral_there() { mouse_click(&mut h, two.min + egui::vec2(100.0, 60.0)); assert_eq!(h.state().views[0].camera.map(|q| q.page), Some(1)); } + +/// A camera click is located where it came up, not where the pointer went after it in the same +/// frame: pressed on page 2 and released on page 1 (the gaps hidden), it moves nothing, though the +/// pointer is back on page 2 by the frame's end. The pointer's last position used to move the +/// quadrilateral to page 2. +#[test] +fn a_camera_click_released_off_its_page_moves_nothing() { + let (mut h, seam) = touching_pages(|app| app.quick_tool = QuickTool::EnhanceCamera); + assert_eq!(h.state().views[0].camera.map(|q| q.page), Some(0), "it starts on the current page"); + let (press, release, back) = (seam + egui::vec2(0.0, 2.0), seam - egui::vec2(0.0, 2.0), seam + egui::vec2(0.0, 40.0)); + h.hover_at(press); + h.run_steps(1); + h.drag_at(press); + h.run_steps(1); + let up = egui::Event::PointerButton { pos: release, button: egui::PointerButton::Primary, pressed: false, modifiers: egui::Modifiers::NONE }; + one_frame(&mut h, [up, egui::Event::PointerMoved(back)]); + h.run_steps(2); + assert_eq!(h.state().views[0].camera.map(|q| q.page), Some(0)); +}