From c10f87f673b3b4d8f85cbd333387d6f1a1696f36 Mon Sep 17 00:00:00 2001 From: alehel Date: Tue, 3 Nov 2026 08:38:18 +0100 Subject: [PATCH] fix(ui): a comment's gesture whose release the pages missed applies nothing `fix(ui): a comment's move, resize or reshape ends` ended a comment's move, resize or reshape once the button was up, and applied it where the pointer then was: when the pages missed the release (the page scrolled out of range, a tab switch, a tool switched mid-drag), the comment jumped to wherever the pointer next showed up over its page, and while the pointer stayed off the pages the gesture waited for it. Only a drag that stops on the page applies its gesture now; one that ends with the button up and no drag stopped there ends with nothing applied, whether the pointer is on the page or not. The test hovers away from the move's start, where the jump shows. Found in review. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/tools/comments/input.rs | 14 ++++++++++---- crates/ui-egui/tests/it/pointer.rs | 10 ++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/tools/comments/input.rs b/crates/ui-egui/src/tools/comments/input.rs index d1218cc08b..df05b9a635 100644 --- a/crates/ui-egui/src/tools/comments/input.rs +++ b/crates/ui-egui/src/tools/comments/input.rs @@ -226,12 +226,18 @@ fn select_input( CommentPart::Body => {} } } - if matches!(cv.gesture, Some(Gesture::Move { page, .. } | Gesture::Resize { page, .. } | Gesture::Reshape { page, .. }) if page == cx.page) { + let live = + matches!(cv.gesture, Some(Gesture::Move { page, .. } | Gesture::Resize { page, .. } | Gesture::Reshape { page, .. }) if page == cx.page); + if live { consumed = true; } - // A move, resize or reshape ends where its drag stops, or once the button is up when the pages - // missed its release. - if (resp.drag_stopped() || !cx.ptr.primary_down) + // A move, resize or reshape ends where its drag stops. One whose release the pages missed (the + // button is up, and no drag stopped here) ends with nothing applied: where it came up is not + // known, and the pointer may be anywhere by now. + if live && !resp.drag_stopped() && !cx.ptr.primary_down { + cv.gesture = None; + } + if resp.drag_stopped() && let Some(p) = pointer { match cv.gesture.clone() { diff --git a/crates/ui-egui/tests/it/pointer.rs b/crates/ui-egui/tests/it/pointer.rs index 9b3fd665c7..72b9602828 100644 --- a/crates/ui-egui/tests/it/pointer.rs +++ b/crates/ui-egui/tests/it/pointer.rs @@ -1639,9 +1639,9 @@ fn a_right_click_selects_its_comment_though_a_gesture_was_left_over() { assert_eq!(h.state().views[0].comments.selected, Some((0, 1)), "the note under the right-click is selected"); } -/// A comment's move ends once its button is up, though the pages missed its release: a drag elsewhere -/// later moves nothing. The move waited for the next drag's end on the page, and took the comment -/// there. +/// A comment's move ends once its button is up, though the pages missed its release, and applies +/// nothing: where it came up is not known, and the pointer has moved on. A drag elsewhere later moves +/// nothing either. The move waited for the next drag's end on the page, and took the comment there. #[test] fn a_comment_move_whose_release_the_pages_missed_ends_once_the_button_is_up() { let mut h = open(stacked_comments_pdf(), "150", false); @@ -1652,9 +1652,11 @@ fn a_comment_move_whose_release_the_pages_missed_ends_once_the_button_is_up() { let before = rect(&h); let bottom = at(&h, 60.0, 160.0); h.state_mut().views[0].comments.gesture = Some(pdfcraft_ui_egui::comments::Gesture::Move { page: 0, index: 0, from: bottom }); - h.hover_at(bottom); + let elsewhere = at(&h, 250.0, 100.0); + h.hover_at(elsewhere); h.run_steps(2); assert!(h.state().views[0].comments.gesture.is_none(), "the move is over"); + assert_eq!(rect(&h), before, "and moved nothing to where the pointer turned up"); let blank = at(&h, 250.0, 60.0); mouse_drag(&mut h, blank, egui::vec2(30.0, 0.0)); assert_eq!(rect(&h), before, "nothing moved the comment");