Align: a rejected select.key keeps the key anchor that was set

select.key {id, anchor} set the new key anchor before checking it, so a
point that is in the selection but not on the path cleared the key
anchor and then returned an error. The point is now checked first: an
anchor the path does not have is not a selected anchor point, and the
other conditions are checked on a copy of the selection.
This commit is contained in:
dotCooCoo
2026-11-14 23:04:17 -08:00
parent 53249e406c
commit 5e4794a65c
2 changed files with 23 additions and 4 deletions
+10 -4
View File
@@ -670,14 +670,20 @@ fn key_anchor(s: &mut Session, id: Option<NodeId>, guide: Option<usize>, v: &Val
return Err(bad(C, "give `anchor` or `guide`, not both"));
}
let id = id.ok_or_else(|| bad(C, "`anchor` needs `id`, the path the point is on"))?;
let sel = &s.doc()?.selection;
if sel.segments.contains_key(&id) || !sel.anchors.get(&id).is_some_and(|set| set.contains(&r)) {
let st = s.doc()?;
let sel = &st.selection;
let on_path = st.doc.node(id).and_then(|n| n.path_data()).is_some_and(|p| p.subpaths.get(r.0).is_some_and(|sp| r.1 < sp.anchors.len()));
if !on_path || sel.segments.contains_key(&id) || !sel.anchors.get(&id).is_some_and(|set| set.contains(&r)) {
return Err(bad(C, format!("anchor [{}, {}] of object {id} is not a selected anchor point", r.0, r.1)));
}
s.select(|_, sel| sel.key_anchor = Some((id, r)))?;
if s.doc()?.selection.key_anchor.is_none() {
// Check the new key on a copy first, so a request that fails leaves the key as it was.
let mut trial = sel.clone();
trial.key_anchor = Some((id, r));
trial.prune(&st.doc);
if trial.key_anchor.is_none() {
return Err(bad(C, "select another anchor point to align to the key anchor"));
}
s.select(|_, sel| sel.key_anchor = Some((id, r)))?;
key_result(s)
}
+13
View File
@@ -147,3 +147,16 @@ fn select_key_rejects_an_anchor_that_cannot_be_the_key() {
assert!(err(&mut s, json!({"id": a.0, "anchor": [0, 0]})).contains("is not a selected anchor point"));
assert_eq!(key_anchor(&s), None, "the key stays as it was");
}
#[test]
fn a_rejected_select_key_keeps_the_key_anchor() {
let mut s = session();
let a = path(&mut s, "M100 40 L200 60 L150 160 Z");
s.execute("select.anchors", &json!({"id": a.0, "anchors": [[0, 0], [0, 9]], "mode": "set"})).unwrap();
s.execute("select.anchors", &json!({"id": a.0, "anchors": [[0, 1]], "mode": "add"})).unwrap();
assert_eq!(key_anchor(&s), Some((a, (0, 1))));
// [0, 9] is in the selection but not on the path.
let err = s.execute("select.key", &json!({"id": a.0, "anchor": [0, 9]})).expect_err("a point the path does not have").to_string();
assert!(err.contains("is not a selected anchor point"), "{err}");
assert_eq!(key_anchor(&s), Some((a, (0, 1))), "the key stays as it was");
}