mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 21:57:12 -06:00
Direct Selection: a marquee selects area type's frame anchors
Squashed from #4435 onto main.
This commit is contained in:
@@ -7,7 +7,7 @@ use vectorcraft_doc::{NodeKind, TextKind, TextObject};
|
||||
use vectorcraft_geom::{Affine, Point, Rect};
|
||||
use vectorcraft_text::TextLayout;
|
||||
use vectorcraft_tools::bbox::Handle;
|
||||
use vectorcraft_tools::{Mods, PointerEvent, PointerKind};
|
||||
use vectorcraft_tools::{Mods, PointerEvent, PointerKind, ToolKey};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@@ -194,6 +194,87 @@ fn direct_selection_drags_a_frame_corner_or_edge() {
|
||||
assert_eq!(text(&s, id).runs[0].style.size, 12.0);
|
||||
}
|
||||
|
||||
/// Direct Selection's marquee selects the frame anchors of area type inside it: a drag of one moves
|
||||
/// only those, the arrow keys nudge them, and Shift-click takes one out.
|
||||
#[test]
|
||||
fn direct_selection_marquee_selects_frame_anchors() {
|
||||
let mut s = session();
|
||||
let id = area(&mut s, 40.0, 40.0, 120.0, 40.0, STORY);
|
||||
let other = area(&mut s, 300.0, 40.0, 120.0, 40.0, STORY);
|
||||
s.execute("select.none", &json!({})).unwrap();
|
||||
let v = ViewInfo::default();
|
||||
let shift = Mods { shift: true, ..Mods::default() };
|
||||
// A marquee round the right-hand corners, (160, 40) and (160, 80).
|
||||
drag(&mut s, "directSelection", Point::new(150.0, 20.0), Point::new(180.0, 100.0));
|
||||
assert_eq!(s.doc().unwrap().selection.objects, vec![id]);
|
||||
let n = undo_len(&s);
|
||||
drag(&mut s, "directSelection", Point::new(160.0, 80.0), Point::new(180.0, 100.0));
|
||||
assert_eq!(undo_len(&s), n + 1, "one step");
|
||||
assert_eq!(last_step(&s), "Reshape Type Area");
|
||||
assert_eq!(corners(&s, id), vec![Point::ZERO, Point::new(140.0, 20.0), Point::new(140.0, 60.0), Point::new(0.0, 40.0)]);
|
||||
// The arrow keys nudge them, each press one step, and Shift-click takes (179, 60) out.
|
||||
assert!(s.tool_claims_key(ToolKey::Left, v));
|
||||
s.tool_key(ToolKey::Left, Mods::default(), v).unwrap();
|
||||
assert_eq!(undo_len(&s), n + 2);
|
||||
assert_eq!(corners(&s, id), vec![Point::ZERO, Point::new(139.0, 20.0), Point::new(139.0, 60.0), Point::new(0.0, 40.0)]);
|
||||
for kind in [PointerKind::Down, PointerKind::Up] {
|
||||
s.pointer(&PointerEvent::new(kind, 179.0, 60.0).with_mods(shift), v).unwrap();
|
||||
}
|
||||
s.tool_key(ToolKey::Down, shift, v).unwrap();
|
||||
assert_eq!(corners(&s, id), vec![Point::ZERO, Point::new(139.0, 20.0), Point::new(139.0, 70.0), Point::new(0.0, 40.0)]);
|
||||
// A marquee across both frames' facing corners moves both frames in one step.
|
||||
drag(&mut s, "directSelection", Point::new(170.0, 10.0), Point::new(310.0, 65.0));
|
||||
assert_eq!(s.doc().unwrap().selection.objects.len(), 2);
|
||||
let n = undo_len(&s);
|
||||
drag(&mut s, "directSelection", Point::new(300.0, 40.0), Point::new(300.0, 30.0));
|
||||
assert_eq!(undo_len(&s), n + 1, "one step");
|
||||
assert_eq!(corners(&s, id)[1], Point::new(139.0, 10.0));
|
||||
assert_eq!(corners(&s, other), vec![Point::new(0.0, -10.0), Point::new(120.0, 0.0), Point::new(120.0, 40.0), Point::new(0.0, 40.0)]);
|
||||
// Shift-click on the other frame's only selected corner deselects that type; the corner of the
|
||||
// first frame still nudges.
|
||||
for kind in [PointerKind::Down, PointerKind::Up] {
|
||||
s.pointer(&PointerEvent::new(kind, 300.0, 30.0).with_mods(shift), v).unwrap();
|
||||
}
|
||||
assert_eq!(s.doc().unwrap().selection.objects, vec![id]);
|
||||
s.tool_key(ToolKey::Up, Mods::default(), v).unwrap();
|
||||
assert_eq!(corners(&s, id)[1], Point::new(139.0, 9.0));
|
||||
// Deselect, then the same two selected again: they are selected whole, so the arrow keys move
|
||||
// them as objects.
|
||||
s.execute("select.none", &json!({})).unwrap();
|
||||
sel(&mut s, &[id, other]);
|
||||
assert!(!s.tool_claims_key(ToolKey::Left, v));
|
||||
// A click on empty canvas deselects; the arrow keys are then no longer the tool's.
|
||||
drag(&mut s, "directSelection", Point::new(600.0, 500.0), Point::new(600.0, 500.0));
|
||||
assert!(!s.tool_claims_key(ToolKey::Left, v));
|
||||
}
|
||||
|
||||
/// `text.reshapeArea` with `items` moves several frames' anchors in one step, and fails whole.
|
||||
#[test]
|
||||
fn reshape_area_moves_the_anchors_of_several_frames() {
|
||||
let mut s = session();
|
||||
let a = area(&mut s, 40.0, 40.0, 120.0, 40.0, STORY);
|
||||
let b = area(&mut s, 300.0, 40.0, 120.0, 40.0, STORY);
|
||||
let n = undo_len(&s);
|
||||
let items = json!([{"id": a.0, "anchors": [[0, 2]]}, {"id": b.0, "anchors": [[0, 0], [0, 3]]}]);
|
||||
s.execute("text.reshapeArea", &json!({"items": items, "dx": -10, "dy": 5})).unwrap();
|
||||
assert_eq!(undo_len(&s), n + 1, "one step");
|
||||
assert_eq!(corners(&s, a)[2], Point::new(110.0, 45.0));
|
||||
assert_eq!(corners(&s, b), vec![Point::new(-10.0, 5.0), Point::new(120.0, 0.0), Point::new(120.0, 40.0), Point::new(-10.0, 45.0)]);
|
||||
for bad in [
|
||||
json!({"items": [], "dx": 1, "dy": 1}),
|
||||
json!({"items": {"id": a.0}, "dx": 1, "dy": 1}),
|
||||
json!({"items": [{"id": a.0, "anchors": [[0, 2]]}], "id": a.0, "dx": 1, "dy": 1}),
|
||||
json!({"items": [{"id": a.0, "anchors": [[0, 2]]}, {"id": a.0, "anchors": [[0, 1]]}], "dx": 1, "dy": 1}),
|
||||
// A bad item fails the whole step: the good one does not move either.
|
||||
json!({"items": [{"id": a.0, "anchors": [[0, 2]]}, {"id": b.0, "anchors": [[0, 9]]}], "dx": 1, "dy": 1}),
|
||||
json!({"items": [{"id": a.0, "anchors": [[0, 2]]}, {"anchors": [[0, 1]]}], "dx": 1, "dy": 1}),
|
||||
] {
|
||||
assert!(s.execute("text.reshapeArea", &bad).is_err(), "{bad}");
|
||||
}
|
||||
assert_eq!(undo_len(&s), n + 1, "nothing more changed");
|
||||
assert_eq!(corners(&s, a)[2], Point::new(110.0, 45.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resized_frame_reflows_its_thread() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user