From 012a50815f8f764d2155a40c43779fa8d731ca68 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 1 Nov 2026 19:25:46 -0800 Subject: [PATCH] Select: Link Block Series skips hidden and locked type in selected groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Select › Same › Link Block Series no longer counts hidden or locked type inside a selected group, or type inside a hidden or locked object there, as selected. A group that holds only such type leaves the command off, and the command does not select the other frames of that type's thread. --- crates/engine/src/cmd/docmenu.rs | 22 +++++++++++++++------- crates/engine/src/tests/menucmds.rs | 15 +++++++++++++++ docs/mcp.md | 3 ++- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/crates/engine/src/cmd/docmenu.rs b/crates/engine/src/cmd/docmenu.rs index 181864a6c..41b8f3de5 100644 --- a/crates/engine/src/cmd/docmenu.rs +++ b/crates/engine/src/cmd/docmenu.rs @@ -32,7 +32,7 @@ pub fn specs() -> Vec { "Link Block Series", ["Select", "Same"], None, - "{} select every frame of the threads of the selected type objects (and of the type inside selected groups); type in no thread selects itself; frames that are hidden or locked, inside a hidden or locked object or layer, on a template layer, or outside the isolated group or layer are left out → {count}", + "{} select every frame of the threads of the selected type objects (and of the visible, unlocked type inside selected groups); type in no thread selects itself; frames that are hidden or locked, inside a hidden or locked object or layer, on a template layer, or outside the isolated group or layer are left out → {count}", has_text_selection, same_link_block_series ), @@ -322,16 +322,24 @@ fn same_text(s: &mut Session, cmd: &str, eq: fn(&CharStyle, &CharStyle) -> bool) finish_same(s, cmd, ids) } -/// The type objects among the selected objects and inside them, in selection order. +/// The type objects among the selected objects and inside them, in selection order, leaving out +/// every hidden or locked object with what it holds. fn selected_text(s: &Session) -> Vec { + fn inside(n: &Node, out: &mut Vec) { + if !n.visible || n.locked { + return; + } + if matches!(n.kind, NodeKind::Text(_)) && !out.contains(&n.id) { + out.push(n.id); + } + for c in n.children().into_iter().flatten() { + inside(c, out); + } + } let Some(st) = s.active() else { return vec![] }; let mut out = vec![]; for n in st.selection.objects.iter().filter_map(|id| st.doc.node(*id)) { - n.walk(&mut |c| { - if matches!(c.kind, NodeKind::Text(_)) && !out.contains(&c.id) { - out.push(c.id); - } - }); + inside(n, &mut out); } out } diff --git a/crates/engine/src/tests/menucmds.rs b/crates/engine/src/tests/menucmds.rs index 9f6a68ab1..a3ad38a98 100644 --- a/crates/engine/src/tests/menucmds.rs +++ b/crates/engine/src/tests/menucmds.rs @@ -1978,6 +1978,21 @@ fn select_same_link_block_series_selects_the_frames_of_a_thread() { // With no type selected the command is not available. sel(&mut s, &[r]); assert!(s.execute("select.same.linkBlockSeries", &json!({})).is_err()); + // A selected group counts the type inside it, but not hidden or locked type. + let box_ = rect(&mut s, 400.0, 400.0, 10.0, 10.0); + sel(&mut s, &[d, box_]); + let g = id_of(&s.execute("object.group", &json!({})).unwrap()); + sel(&mut s, &[g]); + assert!(find_command("select.same.linkBlockSeries").unwrap().info(&s).enabled); + assert_eq!(s.execute("select.same.linkBlockSeries", &json!({})).unwrap()["count"], 2); + assert_eq!(selected(&s), vec![d, e]); + for props in [json!({"id": d.0, "visible": false}), json!({"id": d.0, "visible": true, "locked": true})] { + s.execute("layer.setProps", &props).unwrap(); + sel(&mut s, &[g]); + assert!(!find_command("select.same.linkBlockSeries").unwrap().info(&s).enabled, "{props}"); + assert!(s.execute("select.same.linkBlockSeries", &json!({})).is_err(), "{props}"); + assert_eq!(selected(&s), vec![g], "{props}"); + } } /// #1737: Select > Same reads type's fill and stroke from its characters (the first run), so type diff --git a/docs/mcp.md b/docs/mcp.md index 26732cee6..174960488 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -4886,7 +4886,8 @@ doesn't exist, a `port` other than `in` or `out`, and a side where no frame of a `select.same.linkBlockSeries` (Select › Same › Link Block Series) selects every frame of the threads that the selected type objects, and the type inside selected groups, are in, in thread order, and returns `{count}`. Type in no thread selects itself, and other selected objects are left out. With frames of several threads selected, it selects the frames -of all of them. It is off while no type is selected, and records no undo step. +of all of them. Hidden or locked type inside a selected group does not count as selected, and hidden or locked frames of +a thread are left out. It is off while no type is selected, and records no undo step. In every thread, a paragraph that continues from one frame into the next has its first line, first-line indent and bullet or number in the frame where it starts. In the next frame its first line starts where its wrapped lines start,