fix(ui): a camera click is located where it came up

Found in review (CodeRabbit): the last fix located a camera click by the pointer, which can have
moved on by the end of the release's frame. A click pressed on page 2 and released on page 1,
with the pointer back on page 2 by then, moved the quadrilateral to page 2. `PagePointer` keeps
each release's position, so `PagePointer::released` gives where the click came up, and the click
is located there.

Test: `a_camera_click_released_off_its_page_moves_nothing`, seen failing without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
alehelandClaude Opus 5.5 committed 2026-11-02 22:04:19 +01:00
1 parent b77903012d
commit bdd2ff0ee6
3 files changed
+31 -2

No files matched your search

@@ -78,6 +78,15 @@ impl PagePointer {
pub(crate) fn pressed(&self) -> Option<Pos2> {
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<Pos2> {
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
+3 -2
View File
@@ -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))
+19
View File
@@ -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));
}