M6.7: Object → Clipping Mask → Edit Contents / Edit Clipping Path (select the clipped art or the mask of the selected clip groups, also from inside one)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Brandon ThomasandClaude Opus 5.5 committed 2026-10-01 20:56:22 -04:00
1 parent a5a02f43c0
commit f5ff9317d4
3 files changed
+59 -1

No files matched your search

+45
View File
@@ -89,6 +89,24 @@ pub fn specs() -> Vec<CommandSpec> {
cmd!("object.compoundPath.release", "Release", ["Object", "Compound Path"], Some("Cmd+Alt+Shift+8"), "{}", has_selection, compound_release),
cmd!("object.clippingMask.make", "Make", ["Object", "Clipping Mask"], Some("Cmd+7"), "{}", has_multi, clip_make),
cmd!("object.clippingMask.release", "Release", ["Object", "Clipping Mask"], Some("Cmd+Alt+7"), "{}", has_selection, clip_release),
cmd!(
"object.clippingMask.editContents",
"Edit Contents",
["Object", "Clipping Mask"],
None,
"{} select the clipped art of the selected clip groups → {count}",
has_selection,
|s, _| clip_edit(s, false)
),
cmd!(
"object.clippingMask.editMask",
"Edit Clipping Path",
["Object", "Clipping Mask"],
None,
"{} select the clipping paths of the selected clip groups → {count}",
has_selection,
|s, _| clip_edit(s, true)
),
cmd!("object.isolate", "Enter Isolation Mode", [], None, "{id}", has_doc, isolate),
cmd!("object.exitIsolation", "Exit Isolation Mode", [], None, "{}", has_doc, exit_isolation),
cmd!(
@@ -543,6 +561,33 @@ fn clip_release(s: &mut Session, _: &Value) -> Result<Value> {
ok()
}
/// Selects the clipping path (`mask`) or the clipped contents of every selected clip group (or of the
/// clip group containing a selected object), like Illustrator's Edit Contents / Edit Clipping Path toggle.
fn clip_edit(s: &mut Session, mask: bool) -> Result<Value> {
let st = s.doc()?;
let mut ids = vec![];
for id in st.selection.objects.iter().copied() {
let mut cur = Some(id);
while let Some(c) = cur {
if let Some(NodeKind::Group { children, clip: true }) = st.doc.node(c).map(|n| &n.kind) {
if mask {
ids.extend(children.first().map(|c| c.id));
} else {
ids.extend(children.iter().skip(1).map(|c| c.id));
}
break;
}
cur = st.doc.position(c).and_then(|(par, _, _)| par);
}
}
ids.dedup();
if ids.is_empty() {
return Err(EngineError::Other("select a clip group".into()));
}
s.select(|_, sel| sel.set(ids.iter().copied()))?;
Ok(json!({ "count": ids.len() }))
}
fn isolate(s: &mut Session, p: &Value) -> Result<Value> {
let id = id_param(p, "id")
.or_else(|| s.active().and_then(|d| d.selection.objects.first().copied()))
+5
View File
@@ -178,6 +178,11 @@ fn clipping_and_compound() {
assert_eq!(s.doc().unwrap().selection.len(), 2);
let g = s.execute("object.clippingMask.make", &json!({})).unwrap()["id"].as_u64().unwrap();
assert_eq!(s.doc().unwrap().doc.node(NodeId(g)).unwrap().kind_label(), "Clip Group");
// Edit Contents selects the clipped art; Edit Clipping Path the mask (also from inside the group).
s.execute("object.clippingMask.editContents", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().selection.objects, vec![a]);
s.execute("object.clippingMask.editMask", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().selection.objects, vec![b]);
}
#[test]
+9 -1
View File
@@ -782,7 +782,15 @@ pub fn menu_tree() -> Vec<(&'static str, Vec<Item>)> {
),
sub("Text Wrap", vec![todo("Make"), todo("Release"), todo("Text Wrap Options…")]),
Sep,
sub("Clipping Mask", vec![c("Make", "object.clippingMask.make"), c("Release", "object.clippingMask.release"), todo("Edit Contents")]),
sub(
"Clipping Mask",
vec![
c("Make", "object.clippingMask.make"),
c("Release", "object.clippingMask.release"),
c("Edit Contents", "object.clippingMask.editContents"),
c("Edit Clipping Path", "object.clippingMask.editMask"),
],
),
sub("Compound Path", vec![c("Make", "object.compoundPath.make"), c("Release", "object.compoundPath.release")]),
sub(
"Artboards",