mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 03:41:28 +00:00
Shape tools: guides take the centre point's pointer too, and more regressions
The Selection tool's cursor now checks the ruler guides before a selected object's centre point, as a press does. Tests: a ruler guide under the centre point keeps the pointer and drags; on a box too thin for its centre to clear the handles the handles take both; a shape-tool move from the centre snaps and shows the hollow arrow; and Alt pressed mid-drag copies the shape through the engine.
This commit is contained in:
committed by
bflatastic
parent
c36971afe9
commit
6d3c0fef99
@@ -94,6 +94,20 @@ fn a_shape_tool_moves_the_shape_from_its_centre_point() {
|
||||
assert_eq!(bounds(&s, copy), at, "back where it was");
|
||||
}
|
||||
|
||||
/// Alt pressed once the drag has begun turns the move into a copy, as the Selection tool's.
|
||||
#[test]
|
||||
fn alt_pressed_mid_drag_copies_the_shape() {
|
||||
let mut s = new_doc();
|
||||
let id = drawn_rect(&mut s);
|
||||
let before = bounds(&s, id);
|
||||
gesture(&mut s, NONE, &[(PointerKind::Down, 150.0, 140.0), (PointerKind::Drag, 170.0, 140.0)]);
|
||||
gesture(&mut s, Mods { alt: true, ..NONE }, &[(PointerKind::Drag, 200.0, 140.0), (PointerKind::Up, 200.0, 140.0)]);
|
||||
assert_eq!(s.doc().unwrap().doc.layers[0].children().map(|c| c.len()).unwrap_or_default(), 2, "a copy");
|
||||
assert_eq!(bounds(&s, id), before, "the original stays");
|
||||
let copy = *s.doc().unwrap().selection.objects.first().expect("the copy is selected");
|
||||
assert_eq!(bounds(&s, copy), before + Vec2::new(50.0, 0.0), "the copy moved by the whole drag");
|
||||
}
|
||||
|
||||
/// A press in the rotate zone just outside a corner of the shape's box turns it about that box's
|
||||
/// centre, as the Selection tool's does; a press on the corner itself still rounds it.
|
||||
#[test]
|
||||
|
||||
@@ -1128,15 +1128,15 @@ impl Tool for SelectionTool {
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
// A selected object's centre point (Show Center): a press there moves it, where the art
|
||||
// may not reach. The box's handles take a press before it, so they take the pointer too
|
||||
// (on a thin box the centre lies within their reach).
|
||||
if near_selected && hit_center(cx, p, cx.point_tol()).is_some() {
|
||||
return Cursor::Move;
|
||||
}
|
||||
if let Some(c) = self.guide.cursor(cx, p) {
|
||||
return c;
|
||||
}
|
||||
// A selected object's centre point (Show Center): a press there moves it, where the art
|
||||
// may not reach. The box's handles and the ruler guides take a press before it, so they
|
||||
// take the pointer too (on a thin box the centre lies within a handle's reach).
|
||||
if near_selected && hit_center(cx, p, cx.point_tol()).is_some() {
|
||||
return Cursor::Move;
|
||||
}
|
||||
// View → Snap to Point: over an anchor a press would grab the object by, the arrow turns
|
||||
// hollow.
|
||||
if cx.snap_to_point && matches!(Self::hover_point(cx, p), Some((_, "anchor"))) {
|
||||
|
||||
@@ -2199,3 +2199,54 @@ fn a_shift_press_on_the_centre_point_moves_or_deselects() {
|
||||
vec![Action::Exec("select.toggle".into(), json!({"id": id.0}))]
|
||||
);
|
||||
}
|
||||
|
||||
/// On a box too thin for its centre point to clear the handles, the handles take the pointer and
|
||||
/// the press, as they take the press itself: the centre is within their 5 pt reach.
|
||||
#[test]
|
||||
fn a_thin_shapes_centre_point_gives_the_handles_the_pointer() {
|
||||
use vectorcraft_doc::{Appearance, LiveShape, Node, NodeKind};
|
||||
let shape = LiveShape::Rectangle {
|
||||
w: 100.0,
|
||||
h: 8.0,
|
||||
radii: [0.0; 4],
|
||||
kinds: Default::default(),
|
||||
radii_relative: false,
|
||||
xf: vectorcraft_geom::Affine::translate((100.0, 100.0)),
|
||||
};
|
||||
let mut d = vectorcraft_doc::Document::new(500.0, 500.0);
|
||||
let (layer, id) = (d.layers[0].id, d.alloc_id());
|
||||
let mut n = Node::path(id, shape.to_path(), Appearance::default());
|
||||
if let NodeKind::Path { live: slot, .. } = &mut n.kind {
|
||||
*slot = Some(Box::new(shape));
|
||||
}
|
||||
d.insert(Some(layer), 0, n).unwrap();
|
||||
let mut s = Selection::default();
|
||||
s.add(id);
|
||||
let p = paint();
|
||||
let c = cx(&d, &s, &p);
|
||||
let centre = Point::new(150.0, 104.0);
|
||||
assert!(hit_center(&c, centre, c.point_tol()).is_some(), "the centre point is there");
|
||||
let mut t = SelectionTool::default();
|
||||
assert_eq!(t.cursor(&c, centre, Mods::default()), Cursor::ResizeV, "the top and bottom handles take it");
|
||||
assert_eq!(t.pointer(&c, &ev(PointerKind::Down, centre.x, centre.y)), vec![Action::Begin("Scale".into())]);
|
||||
}
|
||||
|
||||
/// A ruler guide under a selected object's centre point keeps the pointer and the press, as it
|
||||
/// keeps them over the art.
|
||||
#[test]
|
||||
fn a_ruler_guide_under_the_centre_point_keeps_the_pointer() {
|
||||
let (mut d, id) = doc_with_live_rect();
|
||||
d.guides.push(vectorcraft_doc::Guide::new(true, 150.0));
|
||||
let mut s = Selection::default();
|
||||
s.add(id);
|
||||
let p = paint();
|
||||
let c = cx(&d, &s, &p);
|
||||
let centre = Point::new(150.0, 150.0);
|
||||
assert!(hit_center(&c, centre, c.point_tol()).is_some());
|
||||
let mut t = SelectionTool::default();
|
||||
let cursor = t.cursor(&c, centre, Mods::default());
|
||||
assert_ne!(cursor, Cursor::Move, "the guide's cursor, not the move arrow");
|
||||
let down = t.pointer(&c, &ev(PointerKind::Down, centre.x, centre.y));
|
||||
let drag = t.pointer(&c, &ev(PointerKind::Drag, 170.0, 150.0));
|
||||
assert!(drag.iter().any(|a| matches!(a, Action::Preview(cmd, _) if cmd == "guide.move")), "the guide drags: {down:?} {drag:?}");
|
||||
}
|
||||
|
||||
@@ -550,3 +550,29 @@ fn a_live_corner_widget_wins_over_another_shapes_centre_point() {
|
||||
let a = t.pointer(&c, &PointerEvent::new(PointerKind::Drag, 160.0, 160.0));
|
||||
assert_eq!(a.first(), Some(&Action::Begin("Corner Radius".into())), "{a:?}");
|
||||
}
|
||||
|
||||
/// A move of the selection from the centre point snaps like the Selection tool's, and the arrow
|
||||
/// turns hollow while it does.
|
||||
#[test]
|
||||
fn a_move_from_the_centre_point_snaps_and_shows_the_hollow_arrow() {
|
||||
use vectorcraft_doc::{Appearance, Node};
|
||||
let (mut d, s) = unfilled_live_rect();
|
||||
// Another rectangle whose left edge is at x = 300: the moved selection's right edge lands 4 pt
|
||||
// short of it, within the Smart Guides' reach.
|
||||
let (layer, id) = (d.layers[0].id, d.alloc_id());
|
||||
d.insert(Some(layer), 1, Node::path(id, vectorcraft_geom::shapes::rectangle(Rect::new(300.0, 100.0, 350.0, 180.0)), Appearance::default_art()))
|
||||
.unwrap();
|
||||
let p = paint();
|
||||
let c = cx(&d, &s, &p);
|
||||
let mut t = ShapeTool::new("rectangle");
|
||||
t.pointer(&c, &PointerEvent::new(PointerKind::Down, 150.0, 150.0));
|
||||
let a = t.pointer(&c, &PointerEvent::new(PointerKind::Drag, 246.0, 150.0));
|
||||
assert!(matches!(a.last(), Some(Action::Preview(_, v)) if v["matrix"][4] == json!(100.0)), "snapped to the edge: {a:?}");
|
||||
assert_eq!(t.cursor(&c, Point::new(246.0, 150.0), Mods::default()), Cursor::ArrowHollow);
|
||||
// Away from the neighbour, the move is the pointer's own and the arrow is plain.
|
||||
let mut t = ShapeTool::new("rectangle");
|
||||
t.pointer(&c, &PointerEvent::new(PointerKind::Down, 150.0, 150.0));
|
||||
let a = t.pointer(&c, &PointerEvent::new(PointerKind::Drag, 160.0, 150.0));
|
||||
assert!(matches!(a.last(), Some(Action::Preview(_, v)) if v["matrix"][4] == json!(10.0)), "{a:?}");
|
||||
assert_eq!(t.cursor(&c, Point::new(160.0, 150.0), Mods::default()), Cursor::Arrow);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user