diff --git a/crates/engine/src/tests_bboxrotate.rs b/crates/engine/src/tests_bboxrotate.rs index 334e7b45..902ed629 100644 --- a/crates/engine/src/tests_bboxrotate.rs +++ b/crates/engine/src/tests_bboxrotate.rs @@ -209,3 +209,26 @@ fn rotated_box_rect_matches_page_bounds_when_upright() { sel(&mut s, &[a]); assert_eq!(selection_box(&s), OrientedBox::aligned(Rect::new(10.0, 20.0, 40.0, 60.0))); } + +/// The Selection tool says when a drag moves, scales or rotates the selection (the canvas hides the +/// bounding box meanwhile, #712), and not on a press that hasn't moved or a marquee. +#[test] +fn the_selection_tool_reports_a_transforming_drag() { + let mut s = session(); + let id = id_of(&s.execute("shape.rectangle", &json!({"x": 100, "y": 100, "width": 100, "height": 50})).unwrap()); + s.execute("select.set", &json!({"ids": [id.0]})).unwrap(); + let v = ViewInfo::default(); + s.select_tool("selection", v).unwrap(); + let ev = |kind, x: f64, y: f64| PointerEvent::new(kind, x, y).with_mods(Mods::default()); + s.pointer(&ev(PointerKind::Down, 150.0, 125.0), v).unwrap(); + assert!(!s.tool_transforming(), "a press that hasn't moved"); + s.pointer(&ev(PointerKind::Drag, 190.0, 160.0), v).unwrap(); + assert!(s.tool_transforming(), "moving"); + s.pointer(&ev(PointerKind::Up, 190.0, 160.0), v).unwrap(); + assert!(!s.tool_transforming()); + // A marquee on empty canvas isn't a transform. + s.pointer(&ev(PointerKind::Down, 500.0, 400.0), v).unwrap(); + s.pointer(&ev(PointerKind::Drag, 550.0, 450.0), v).unwrap(); + assert!(s.tool_busy() && !s.tool_transforming()); + s.pointer(&ev(PointerKind::Up, 550.0, 450.0), v).unwrap(); +} diff --git a/crates/engine/src/tooling.rs b/crates/engine/src/tooling.rs index bcec57ca..297b008b 100644 --- a/crates/engine/src/tooling.rs +++ b/crates/engine/src/tooling.rs @@ -358,6 +358,11 @@ impl Session { self.tool.busy() } + /// Is the active tool moving, scaling or rotating the selection with a drag? + pub fn tool_transforming(&self) -> bool { + self.tool.transforming() + } + /// Does the active tool take `key` ahead of the shortcuts bound to it (see `Tool::claims_key`)? pub fn tool_claims_key(&mut self, key: ToolKey, view: ViewInfo) -> bool { self.plane_key(key).is_some() || self.with_tool_cx(view, |t, cx| t.claims_key(cx, key)) diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 7c4aef24..62bc634e 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs @@ -517,6 +517,11 @@ pub trait Tool: Send { fn busy(&self) -> bool { false } + /// Is a drag moving, scaling or rotating the selection (the canvas then hides the bounding box, + /// so only the art is seen going with the pointer)? + fn transforming(&self) -> bool { + false + } /// Does the tool take `key` now, ahead of the command shortcuts bound to it (the Gradient tool /// with a stop selected takes Delete and the arrows)? fn claims_key(&self, _cx: &ToolContext, _key: ToolKey) -> bool { diff --git a/crates/tools/src/select.rs b/crates/tools/src/select.rs index f8d547c9..1bcea77c 100644 --- a/crates/tools/src/select.rs +++ b/crates/tools/src/select.rs @@ -163,6 +163,10 @@ impl Tool for SelectionTool { !matches!(self.state, State::Idle) || self.guide.busy() } + fn transforming(&self) -> bool { + matches!(self.state, State::Moving { began: true, .. } | State::Scaling { .. } | State::Rotating { .. }) + } + fn pointer(&mut self, cx: &ToolContext, ev: &PointerEvent) -> Vec { if let Some(out) = self.guide.pointer(cx, ev) { return out; diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 8bd01189..5ec7f10f 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -1541,8 +1541,10 @@ fn print_tiling_overlay(app: &mut VectorcraftApp, p: &egui::Painter, xf: &Xf) { const PRINT_TILING: Color32 = Color32::from_gray(96); fn selection_overlay(app: &mut VectorcraftApp, p: &egui::Painter, xf: &Xf) { - // The Selection tool's bounding box (rotated with the objects after a rotation). + // The Selection tool's bounding box (rotated with the objects after a rotation). It hides while + // a drag moves or resizes the selection, so only the art is seen going with the pointer (#712). let show_box = app.session.tool_id() == "selection" + && !app.session.tool_transforming() && app.ui.view.bounding_box && app.session.active().is_some_and(|st| !st.selection.is_empty() && st.selection.anchors.is_empty()); let bbox = if show_box { app.selection_box() } else { None };