diff --git a/crates/tools/src/gradient.rs b/crates/tools/src/gradient.rs index b35b90b11..1ccb3a367 100644 --- a/crates/tools/src/gradient.rs +++ b/crates/tools/src/gradient.rs @@ -1,6 +1,7 @@ //! Gradient Swatch tool (G): drag across the selected objects (or the object under the pointer) //! to set where their gradient fill starts and ends; Shift constrains the angle to 45° steps. +use designcraft_doc::SpreadRef; use designcraft_geom::{Point, Vec2}; use serde_json::json; @@ -10,8 +11,8 @@ use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, Tool pub struct GradientTool { /// Gradient Feather tool (Shift+G): the drag sets the fade instead of the fill gradient. pub feather: bool, - /// Canvas start point, spread-space start point and the target ids. - drag: Option<(Point, Point, Vec)>, + /// Canvas start point, spread-space start point and the target ids, and the spread. + drag: Option<(Point, Point, Vec, SpreadRef)>, end: Option, } @@ -44,27 +45,27 @@ impl Tool for GradientTool { } else { cx.selection.items.iter().map(|i| i.0).collect() }; - let Some((_, sp)) = cx.layout.spread_at(ev.pos) else { return vec![] }; + let Some((spread, sp)) = cx.layout.spread_at(ev.pos) else { return vec![] }; if ids.is_empty() { return vec![]; } - self.drag = Some((ev.pos, sp, ids)); + self.drag = Some((ev.pos, sp, ids, spread)); self.end = None; vec![Action::Begin(if self.feather { "Gradient Feather".into() } else { "Gradient".into() })] } PointerKind::Drag => { - let Some((start, sp, ids)) = &self.drag else { return vec![] }; + let Some((start, sp, ids, spread)) = &self.drag else { return vec![] }; let end = constrain(*start, ev.pos, ev.mods); self.end = Some(end); if (end - *start).hypot() < cx.tol(2.0) { return vec![]; } - let to = *sp + (end - *start); + let to = *sp + cx.layout.delta_to_spread(*spread, end - *start); let cmd = if self.feather { "object.gradientFeather" } else { "object.gradient" }; vec![Action::Preview(cmd.into(), json!({"ids": ids, "from": [sp.x, sp.y], "to": [to.x, to.y]}))] } PointerKind::Up => { - let moved = self.end.zip(self.drag.as_ref()).is_some_and(|(e, (s, _, _))| (e - *s).hypot() >= cx.tol(2.0)); + let moved = self.end.zip(self.drag.as_ref()).is_some_and(|(e, (s, ..))| (e - *s).hypot() >= cx.tol(2.0)); let was = self.drag.take().is_some(); self.end = None; match (was, moved) { @@ -79,7 +80,7 @@ impl Tool for GradientTool { fn overlays(&self, _cx: &ToolContext) -> Vec { match (&self.drag, self.end) { - (Some((a, _, _)), Some(b)) => vec![Overlay::Line { a: *a, b, color: [0, 0, 0], dashed: false }], + (Some((a, ..)), Some(b)) => vec![Overlay::Line { a: *a, b, color: [0, 0, 0], dashed: false }], _ => vec![], } } diff --git a/crates/tools/src/tests.rs b/crates/tools/src/tests.rs index ee3b362f1..27e8a6639 100644 --- a/crates/tools/src/tests.rs +++ b/crates/tools/src/tests.rs @@ -193,6 +193,32 @@ fn gradient_tool_drag_sets_the_vector() { assert_eq!(t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 220.0 + off.x, 153.0 + off.y)), vec![Action::Commit]); } +#[test] +fn gradient_tool_drag_follows_a_rotated_spread() { + let mut d = Document::new(&NewDocument::default()); + let lid = d.default_layer(); + d.add_text_frame(SpreadRef::Doc(0), Rect::new(100.0, 100.0, 300.0, 200.0), lid, "", ParaFormat::default()).unwrap(); + Arc::make_mut(&mut d.spreads[0]).pages[0].view_rotation = 1; + let (s, c, l) = (Selection::default(), Cache::new(), CanvasLayout::new(&d, false)); + let cx = ctx(&d, &s, &c, &l); + let start = l.to_canvas(SpreadRef::Doc(0), Point::new(150.0, 150.0)); + let mut t = create("gradientSwatch"); + t.pointer(&cx, &PointerEvent::new(PointerKind::Down, start.x, start.y)); + for shift in [false, true] { + let ev = PointerEvent::new(PointerKind::Drag, start.x + 100.0, start.y).with_mods(Mods { shift, ..Default::default() }); + match &t.pointer(&cx, &ev)[0] { + Action::Preview(_, p) => { + let to: Vec = p["to"].as_array().unwrap().iter().map(|v| v.as_f64().unwrap()).collect(); + let want = l.delta_to_spread(SpreadRef::Doc(0), Vec2::new(100.0, 0.0)); + assert!((to[0] - (150.0 + want.x)).abs() < 1e-6 && (to[1] - (150.0 + want.y)).abs() < 1e-6, "{to:?} want {want:?}"); + assert!((to[0] - 150.0).abs() > 1e-6 || (to[1] - 150.0).abs() > 1e-6); + assert!((to[0] - 150.0).abs() < 1e-6, "{to:?}"); + } + other => panic!("{other:?}"), + } + } +} + #[test] fn anchor_tools_emit_path_commands() { let mut d = Document::new(&NewDocument::default());