diff --git a/crates/ui-egui/src/panels/detail.rs b/crates/ui-egui/src/panels/detail.rs index 870fd380d..2b0989e30 100644 --- a/crates/ui-egui/src/panels/detail.rs +++ b/crates/ui-egui/src/panels/detail.rs @@ -1248,6 +1248,14 @@ fn crop_overlay(app: &mut LightcraftApp, ui: &mut egui::Ui, resp: &egui::Respons let s = to_straight(n, d.crop.geometry.angle, frame); d.crop.geometry.rect.contains(Point::new(s.x.clamp(-1.0, 2.0), s.y)) }; + // a crop drag cut short (the tool closed before the release) leaves its gesture behind: with no + // button held it is over + if matches!(app.gesture, Some(Gesture::CropRotate { .. } | Gesture::CropHandle { .. })) + && !resp.drag_stopped() + && !ui.input(|i| i.pointer.any_down()) + { + app.gesture = None; + } // while rotating, the pointer may leave the canvas: it still shows rotation and the angle let rotating = matches!(app.gesture, Some(Gesture::CropRotate { .. })); let pointer = resp.hover_pos().or_else(|| if rotating { ui.input(|i| i.pointer.latest_pos()) } else { None }); diff --git a/crates/ui-egui/src/tests_crop_rotate.rs b/crates/ui-egui/src/tests_crop_rotate.rs index 00178aa71..60d58552d 100644 --- a/crates/ui-egui/src/tests_crop_rotate.rs +++ b/crates/ui-egui/src/tests_crop_rotate.rs @@ -300,3 +300,32 @@ fn moving_the_box_never_shows_rotation() { assert!(!has(&h, "cropRotateCursor")); assert_eq!(angle(&h), 0.0, "nothing rotated"); } + +/// Closing the crop tool in the middle of a rotation (Return) leaves nothing behind: reopened, +/// the box shows the move pointer and no angle. +#[test] +fn a_rotation_cut_short_leaves_nothing_behind() { + let mut h = crop_tool(); + let r = h.request( + "ui.pointer", + json!({"events": [{"kind": "down", "x": -0.05, "y": 0.2}, {"kind": "drag", "x": -0.05, "y": 0.25}, {"kind": "drag", "x": -0.05, "y": 0.3}]}), + T, + ); + assert_eq!(r["ok"], true, "{r}"); + h.settle(SETTLE); + assert!(has(&h, "cropAngleReadout"), "rotating"); + for (method, params) in [ + ("engine.execute", json!({"command": "tool.done"})), + ("ui.pointer", json!({"events": [{"kind": "up", "x": -0.05, "y": 0.3}]})), + ("engine.execute", json!({"command": "panel.crop"})), + ] { + let r = h.request(method, params, T); + assert_eq!(r["ok"], true, "{r}"); + h.settle(SETTLE); + } + let img = h.app.image_rect.expect("the photo on screen"); + hover(&mut h, img.center()); + assert_eq!(h.last_cursor, egui::CursorIcon::Move); + assert!(!has(&h, "cropAngleReadout"), "no angle"); + assert!(!has(&h, "cropRotateCursor"), "no rotation glyph"); +}