fix(ui): a picture's grip and knob take their drag from the press

Found in review. On `main` the selected picture's grips and rotate knob
were drag-only widgets of their own, so their drag began at the press.
Their widgets went, and the page's response is click-and-drag, which calls
it a drag only past egui's click distance: a move of a few pixels resized
nothing. They take their drag from the press again. A click that doesn't
move edits nothing, as before; a drag of the picture itself still waits,
since a click selects.

Tests: `a_short_drag_from_a_picture_grip_resizes_it` (seen failing with
the fix reverted), `a_click_on_a_picture_grip_changes_nothing` (a pin,
seen failing with the release planted to edit always).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
alehelandClaude Opus 5.5 committed 2026-11-04 19:37:47 +01:00
1 parent fd091beaab
commit dd9ec46efc
2 files changed
+39 -3

No files matched your search

@@ -782,13 +782,16 @@ pub(crate) fn image_input(cx: &ImageCx<'_>, images: &[pdfcraft_engine::PageImage
}
// A drag of the selected picture takes what its press was on ([`body_hit`], [`handle_hit`]):
// the rotate knob turns it, a grip resizes it, the picture itself moves.
// the rotate knob turns it, a grip resizes it, the picture itself moves. The knob and the
// grips take it from the press, so a move too short to be a drag of the page counts; the
// picture waits for that, as a click selects.
let at_press = |part| matches!(part, PicturePart::Knob | PicturePart::Corner(_));
if gate
&& ptr.drag_started
&& let Some(o) = ptr.origin
&& let PagePress::Mine(Owner::Picture { index, part }) = press
&& (index == i || part == PicturePart::OverSelected)
&& let Some(s) = view.image_selection.as_mut()
&& (ptr.drag_started || at_press(part) && ptr.pressed().is_some())
&& let Some(s) = view.image_selection.as_mut().filter(|s| s.drag.is_none())
{
match part {
PicturePart::Knob => {
+33
View File
@@ -7435,6 +7435,39 @@ fn a_drag_from_the_selected_picture_under_another_moves_the_selected_one() {
assert_eq!(selected, Some(1), "a click selects the one on top");
}
/// A move too short to be a drag of the page still resizes the selected picture from its grip, as
/// the grip takes its drag from the press.
#[test]
fn a_short_drag_from_a_picture_grip_resizes_it() {
let mut h = picture_pages(false);
let id = h.state().views[0].id;
let before = h.state().session.get(id).unwrap().page_images(0)[0].matrix;
// The picture shows at x 30..160, y 170..250: its lower right grip, moved 3 px.
let grip = page_at(&h, 0, 160.0, 170.0);
h.hover_at(grip);
h.run_steps(1);
h.drag_at(grip);
h.run_steps(1);
h.hover_at(grip + egui::vec2(3.0, 0.0));
h.run_steps(1);
h.drop_at(grip + egui::vec2(3.0, 0.0));
h.run_steps(3);
assert_ne!(h.state().session.get(id).unwrap().page_images(0)[0].matrix, before, "the picture is resized");
}
/// A click on a selected picture's grip that doesn't move resizes nothing: no edit, and the picture
/// stays selected.
#[test]
fn a_click_on_a_picture_grip_changes_nothing() {
let mut h = picture_pages(false);
let id = h.state().views[0].id;
let before = h.state().session.get(id).unwrap().edit_generation();
let grip = page_at(&h, 0, 160.0, 170.0);
slipping_click(&mut h, grip, grip);
assert_eq!(h.state().session.get(id).unwrap().edit_generation(), before, "no edit");
assert!(h.state().views[0].image_selection.is_some(), "the picture stays selected");
}
/// In the page grid, Delete deletes the page chosen there. The picture selected before the grid
/// opened is not on screen, and stays.
#[test]