diff --git a/crates/engine/src/cmd/slices.rs b/crates/engine/src/cmd/slices.rs index cc8598a5c..c2092da0a 100644 --- a/crates/engine/src/cmd/slices.rs +++ b/crates/engine/src/cmd/slices.rs @@ -14,7 +14,7 @@ use vectorcraft_geom::{Rect, shapes}; use super::{ CommandSpec, EngineError, NodeId, Result, Session, bad, bool_or, cmd, f64_or, f64_req, has_doc, has_selection, id_param, ids_param, index, - targets, + switch_or, targets, }; /// The most slices one Divide Slices makes. @@ -593,7 +593,7 @@ fn options(s: &mut Session, p: &Value) -> Result { } fn clip_to_artboard(s: &mut Session, p: &Value) -> Result { - let on = p.get("on").and_then(Value::as_bool).unwrap_or(!s.doc()?.doc.slices_clip_to_artboard); + let on = switch_or(p, "on", !s.doc()?.doc.slices_clip_to_artboard, "object.slice.clipToArtboard")?; if on != s.doc()?.doc.slices_clip_to_artboard { s.edit("Clip to Artboard", |d, _| { d.slices_clip_to_artboard = on; @@ -604,7 +604,7 @@ fn clip_to_artboard(s: &mut Session, p: &Value) -> Result { } fn hide(s: &mut Session, p: &Value) -> Result { - let v = p.get("hidden").and_then(Value::as_bool).unwrap_or(!s.menu.slices_hidden); + let v = switch_or(p, "hidden", !s.menu.slices_hidden, "view.slices.hide")?; s.menu.slices_hidden = v; if v { deselect_in_every_document(s); @@ -613,7 +613,7 @@ fn hide(s: &mut Session, p: &Value) -> Result { } fn lock(s: &mut Session, p: &Value) -> Result { - let v = p.get("locked").and_then(Value::as_bool).unwrap_or(!s.menu.slices_locked); + let v = switch_or(p, "locked", !s.menu.slices_locked, "view.slices.lock")?; s.menu.slices_locked = v; if v { deselect_in_every_document(s); diff --git a/crates/engine/src/tests/slices.rs b/crates/engine/src/tests/slices.rs index 1576f50ce..87d0088d9 100644 --- a/crates/engine/src/tests/slices.rs +++ b/crates/engine/src/tests/slices.rs @@ -593,3 +593,27 @@ fn slice_commands_check_their_params() { assert!(s.doc().unwrap().doc.node(NodeId(a)).is_some(), "deleting an object slice keeps its object"); assert_eq!(run(&mut s, "object.slice.select", json!({}))["selected"], json!([])); } + +#[test] +fn invalid_slice_toggle_flags_do_not_change_state_or_selection() { + for (command, params) in [ + ("object.slice.clipToArtboard", json!({"on": "false"})), + ("view.slices.hide", json!({"hidden": 0})), + ("view.slices.lock", json!({"locked": 1})), + ] { + let mut s = session(); + let id = user_slice(&mut s, [10.0, 10.0, 20.0, 20.0]); + run(&mut s, "select.object.slices", json!({})); + let before_undo = s.doc().unwrap().history.undo.len(); + assert!(s.execute(command, ¶ms).is_err(), "{command} accepted {params}"); + assert!(s.doc().unwrap().doc.slices_clip_to_artboard, "{command} changed the document"); + assert!(!s.slices_hidden(), "{command} hid slices"); + assert!(!s.slices_locked(), "{command} locked slices"); + assert_eq!(s.doc().unwrap().selection.slices, vec![NodeId(id)], "{command} cleared the selection"); + assert_eq!(s.doc().unwrap().history.undo.len(), before_undo, "{command} recorded an undo step"); + } + // Left out, or null as for every command, the flag toggles. + let mut s = session(); + assert_eq!(run(&mut s, "view.slices.hide", Value::Null)["hidden"], true); + assert_eq!(run(&mut s, "view.slices.hide", json!({"hidden": null}))["hidden"], false); +} diff --git a/docs/mcp.md b/docs/mcp.md index ccc9147b7..5c9c5cb45 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -4466,6 +4466,7 @@ deselects the slices in every open document; unlocking or showing them again lea restore the selection that was cleared. While the slices are locked or hidden, a command that selects slices, such as `select.object.slices` (which replies `count: 0`), `object.slice.select`, `object.slice.create`, `edit.undo` or `edit.redo`, leaves them out; a selected object that is an object slice stays selected. +`object.slice.clipToArtboard`, `view.slices.hide`, and `view.slices.lock` reject a flag that is not a boolean. Omitting the flag toggles its current value. ```json {"name":"run_command","arguments":{"command":"object.slice.make","params":{}}}