Canvas: a dropped press keeps what earlier clicks picked

Escape also clears state that earlier clicks set up, so taking Escape for a
dropped press lost it in five tools. Each now drops only the press:

- Blend: the object picked first and the blend being added to are put
  back as they were at the press.
- Dimension: a dropped click picks nothing, and the points or lines picked
  before stay; a dropped drag is canceled as before.
- Width: the drag ends and the width points selected at the press are
  selected again.
- Gradient: a dropped click on a freeform gradient leaves the line being
  drawn open.
- Selection: with the pointer loaded with text, a dropped press threads
  nothing and the text stays loaded; a dropped press on a port loads none.
This commit is contained in:
dotCooCoo
2026-11-03 16:03:39 +07:00
committed by bflatastic
parent a230ef854b
commit 5d0cffbba1
7 changed files with 271 additions and 21 deletions
+35
View File
@@ -1871,6 +1871,41 @@ fn escape_drops_the_loaded_text() {
assert_eq!(t.pointer(&cx, &ev(PointerKind::Up, 30.0, 420.0)), vec![Action::Exec("select.none".into(), json!({}))]);
}
/// A dropped press with the pointer loaded with text threads nothing, and the text stays loaded:
/// the next click threads it. A dropped press on the port loads no text.
#[test]
fn a_dropped_press_keeps_the_text_loaded() {
let (d, id, _, port) = overflowing_area_type();
let mut s = Selection::default();
s.set([id]);
let p = paint();
let cx = cx(&d, &s, &p);
let mut t = SelectionTool::default();
t.pointer(&cx, &ev(PointerKind::Down, port.x, port.y));
t.drop_press(&cx);
t.pointer(&cx, &ev(PointerKind::Up, port.x, port.y));
assert_ne!(t.cursor(&cx, Point::new(30.0, 420.0), Mods::default()), Cursor::LoadedText, "the dropped press loads no text");
t.pointer(&cx, &ev(PointerKind::Down, port.x, port.y));
t.pointer(&cx, &ev(PointerKind::Up, port.x, port.y));
// The engine sends the release after the drop.
t.pointer(&cx, &ev(PointerKind::Down, 20.0, 20.0));
t.pointer(&cx, &ev(PointerKind::Drag, 80.0, 90.0));
assert!(t.drop_press(&cx).is_empty());
assert!(t.pointer(&cx, &ev(PointerKind::Up, 80.0, 90.0)).is_empty(), "the release threads nothing");
assert!(!t.overlays(&cx).iter().any(|o| matches!(o, Overlay::Marquee(_))), "the new frame's outline is removed");
assert_eq!(t.cursor(&cx, Point::new(30.0, 420.0), Mods::default()), Cursor::LoadedText);
// A press on the port that loaded the text puts it down; dropped, it leaves the text loaded.
t.pointer(&cx, &ev(PointerKind::Down, port.x, port.y));
assert!(t.drop_press(&cx).is_empty());
t.pointer(&cx, &ev(PointerKind::Up, port.x, port.y));
assert_eq!(t.cursor(&cx, Point::new(30.0, 420.0), Mods::default()), Cursor::LoadedText, "a dropped press on the port");
t.pointer(&cx, &ev(PointerKind::Down, 30.0, 420.0));
assert_eq!(
t.pointer(&cx, &ev(PointerKind::Up, 30.0, 420.0)),
vec![Action::Exec("text.thread.add".into(), json!({"from": id.0, "at": [30.0, 420.0]}))]
);
}
/// [`doc_with_area_type`], its text fitting its frame; where its in and out ports are.
fn fitting_area_type() -> (vectorcraft_doc::Document, NodeId, Point, Point) {
let (d, id) = doc_with_area_type();