Merge pull request #5224 from dotCooCoo/feat/select-same-link-block-series

Select: Same › Link Block Series selects every frame of a text thread
This commit is contained in:
BFlat
2026-11-02 16:04:33 +07:00
committed by GitHub
33 changed files with 226 additions and 3 deletions
+89 -2
View File
@@ -2,7 +2,7 @@
//! brush strokes, Save/recall selections), View → Guides, and File → Close All / Document Color
//! Mode / File Info.
use std::collections::BTreeSet;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::sync::Arc;
use serde_json::{Value, json};
@@ -11,7 +11,7 @@ use vectorcraft_geom::Vec2;
use super::edit::selected_roots;
use super::pathops::Beside;
use super::select::{selectable_ids, walk_selectable};
use super::select::{is_selectable, selectable_ids, walk_selectable};
use super::{
CommandSpec, EngineError, Point, Result, Session, bad, bool_or, checked_ids_param, cmd, f64_or, f64_req, has_doc, has_selection, id_param, ok,
str_param,
@@ -27,6 +27,15 @@ pub fn specs() -> Vec<CommandSpec> {
_ => false,
}
)),
cmd!(
"select.same.linkBlockSeries",
"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; type and 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 neither count as selected nor are selected → {count}",
has_text_selection,
same_link_block_series
),
cmd!("select.same.fontFamily", "Font Family", ["Select", "Same"], None, "{} → {count}", has_selection, |s, _| same_text(
s,
"select.same.fontFamily",
@@ -313,6 +322,84 @@ fn same_text(s: &mut Session, cmd: &str, eq: fn(&CharStyle, &CharStyle) -> bool)
finish_same(s, cmd, ids)
}
/// Call `f` with each type object among the selected objects and inside them, in selection order,
/// keeping only those [`walk_selectable`] visits: none that is hidden, locked, on a hidden, locked
/// or template layer, or outside the isolated group. A type object inside two selected objects is
/// given twice. The walk stops when `f` returns false.
fn each_selected_text(s: &Session, mut f: impl FnMut(NodeId) -> bool) {
fn inside(n: &Node, f: &mut impl FnMut(NodeId) -> bool) -> bool {
if !n.visible || n.locked {
return true;
}
if matches!(n.kind, NodeKind::Text(_)) && !f(n.id) {
return false;
}
n.children().into_iter().flatten().all(|c| inside(c, f))
}
let Some(st) = s.active() else { return };
let locks = st.doc.locked_artboards();
for id in &st.selection.objects {
if is_selectable(&st.doc, st.isolation, &locks, *id)
&& let Some(n) = st.doc.node(*id)
&& !inside(n, &mut f)
{
return;
}
}
}
/// The type objects [`each_selected_text`] gives, each once, in selection order.
fn selected_text(s: &Session) -> Vec<NodeId> {
let (mut out, mut seen) = (vec![], HashSet::new());
each_selected_text(s, |id| {
if seen.insert(id) {
out.push(id);
}
true
});
out
}
/// A type object is selected, or is inside a selected object.
fn has_text_selection(s: &Session) -> std::result::Result<(), String> {
has_selection(s)?;
let mut any = false;
each_selected_text(s, |_| {
any = true;
false
});
if any { Ok(()) } else { Err("select a text object".into()) }
}
/// Select › Same › Link Block Series: the frames of the threads the selected type objects are
/// in, in thread order; a type object in no thread is its own series.
fn same_link_block_series(s: &mut Session, _: &Value) -> Result<Value> {
const C: &str = "select.same.linkBlockSeries";
let seeds = selected_text(s);
if seeds.is_empty() {
return Err(bad(C, "select a text object"));
}
let st = s.doc()?;
let can = selectable_ids(&st.doc, st.isolation);
// Each frame's thread: the first thread that lists it.
let mut threads: HashMap<NodeId, &[NodeId]> = HashMap::new();
for t in &st.doc.text_threads {
for id in t {
threads.entry(*id).or_insert(t.as_slice());
}
}
let (mut ids, mut seen): (Vec<NodeId>, HashSet<NodeId>) = (vec![], HashSet::new());
for seed in &seeds {
let series = threads.get(seed).copied().unwrap_or(std::slice::from_ref(seed));
for id in series {
if can.contains(id) && seen.insert(*id) {
ids.push(*id);
}
}
}
finish_same(s, C, ids)
}
fn by_kind(s: &mut Session, f: impl Fn(&Node) -> bool) -> Result<Value> {
let ids = candidates(s, f)?;
s.select(|_, sel| sel.set(ids))?;
+19 -1
View File
@@ -4,7 +4,7 @@ use std::collections::BTreeSet;
use serde_json::{Value, json};
use vectorcraft_color::Paint;
use vectorcraft_doc::{Document, Node, NodeId, NodeKind};
use vectorcraft_doc::{Document, LockedArtboards, Node, NodeId, NodeKind};
use vectorcraft_tools::builder::is_live_paint;
use super::wand::{fill_of, stroke_of};
@@ -249,6 +249,24 @@ pub(crate) fn selectable_ids(d: &Document, iso: Option<NodeId>) -> BTreeSet<Node
ids
}
/// Whether [`walk_selectable`] visits `id`, read from the objects that hold it instead of a walk of
/// the document. `locks` is the document's [`Document::locked_artboards`].
pub(crate) fn is_selectable(d: &Document, iso: Option<NodeId>, locks: &LockedArtboards, id: NodeId) -> bool {
let Some(a) = d.ancestry(id) else { return false };
if !a.iter().all(|i| d.node(*i).is_some_and(|n| n.visible && !n.locked && !n.is_template())) {
return false;
}
match iso.and_then(|i| d.node(i)) {
// Inside the isolated container, or the single object isolated.
Some(c) => a.iter().position(|i| *i == c.id).is_some_and(|p| p + 1 < a.len() || !c.is_container()),
None => {
// The object Select All takes: the outermost one that is not a layer.
let top = a.iter().filter_map(|i| d.node(*i)).find(|n| !n.is_layer());
top.is_some_and(|t| locks.is_empty() || !locks.holds(t))
}
}
}
fn all(s: &mut Session, _: &Value) -> Result<Value> {
// While the Type tool edits text, Select All takes all of that text and leaves the art
// selection as it is (Illustrator: text cursor in a text object).
+63
View File
@@ -1948,6 +1948,69 @@ fn select_same_font_size_and_fill() {
assert!(s.execute("select.same.fontSize", &json!({})).is_err());
}
#[test]
fn select_same_link_block_series_selects_the_frames_of_a_thread() {
let mut s = session();
let frame = |s: &mut Session, x: f64, y: f64| {
id_of(&s.execute("text.create", &json!({"x": x, "y": y, "text": "words ".repeat(40), "area": {"width": 80, "height": 30}})).unwrap())
};
let (a, b, c) = (frame(&mut s, 10.0, 10.0), frame(&mut s, 110.0, 10.0), frame(&mut s, 210.0, 10.0));
sel(&mut s, &[a, b, c]);
s.execute("text.thread.create", &json!({})).unwrap();
let (d, e) = (frame(&mut s, 10.0, 200.0), frame(&mut s, 110.0, 200.0));
sel(&mut s, &[d, e]);
s.execute("text.thread.create", &json!({})).unwrap();
let lone = text(&mut s, "alone");
let r = rect(&mut s, 300.0, 300.0, 10.0, 10.0);
let undo = s.doc().unwrap().history.undo.len();
// The middle frame of the first thread selects that thread's three frames, in thread order.
sel(&mut s, &[b]);
assert_eq!(s.execute("select.same.linkBlockSeries", &json!({})).unwrap()["count"], 3);
assert_eq!(selected(&s), vec![a, b, c]);
// Frames of two threads select both threads.
sel(&mut s, &[c, e]);
assert_eq!(s.execute("select.same.linkBlockSeries", &json!({})).unwrap()["count"], 5);
// Type in no thread selects itself; a path beside it is left out.
sel(&mut s, &[lone, r]);
assert_eq!(s.execute("select.same.linkBlockSeries", &json!({})).unwrap()["count"], 1);
assert_eq!(selected(&s), vec![lone]);
assert_eq!(s.doc().unwrap().history.undo.len(), undo, "selecting adds no undo step");
// 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}");
}
s.execute("layer.setProps", &json!({"id": d.0, "locked": false})).unwrap();
// A frame on a hidden layer, selected by id, is not type to start from.
let l1 = s.doc().unwrap().doc.layers[0].id;
s.execute("layer.setProps", &json!({"id": l1.0, "visible": false})).unwrap();
sel(&mut s, &[a]);
assert!(!find_command("select.same.linkBlockSeries").unwrap().info(&s).enabled);
assert!(s.execute("select.same.linkBlockSeries", &json!({})).is_err());
s.execute("layer.setProps", &json!({"id": l1.0, "visible": true})).unwrap();
// In an isolated group, only the frames inside it count and are selected.
s.execute("object.isolate", &json!({"id": g.0})).unwrap();
sel(&mut s, &[d]);
assert_eq!(s.execute("select.same.linkBlockSeries", &json!({})).unwrap()["count"], 1);
assert_eq!(selected(&s), vec![d]);
sel(&mut s, &[a]);
assert!(!find_command("select.same.linkBlockSeries").unwrap().info(&s).enabled);
assert!(s.execute("select.same.linkBlockSeries", &json!({})).is_err());
}
/// #1737: Select > Same reads type's fill and stroke from its characters (the first run), so type
/// matches type and paths both ways.
#[test]
+1
View File
@@ -3270,6 +3270,7 @@ digits Malayalam Малаялам
Graphic Графика
Show all open documents Показване на всички отворени документи
Symbol Instance Екземпляр на символ
Link Block Series Серия свързани блокове
Symbol Libraries Библиотеки със символи
Symbol Screener Tool Инструмент „Филтър на символи“
Symbol Scruncher Tool Инструмент „Свиване на символи“
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -418,6 +418,7 @@
Stroke Weight Debljina poteza
Graphic Style Grafički stil
Symbol Instance Instanca simbola
Link Block Series Niz povezanih blokova
Font Family Porodica fonta
Font Family & Style Porodica i stil fonta
Font Family, Style & Size Porodica, stil i veličina fonta
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4156,6 +4156,7 @@ digits Malayalam Malaiàlam
Switch x/y Intercanvia x/y
Symbol Símbol
Symbol Instance Instància de símbol
Link Block Series Sèrie de blocs enllaçats
Symbol Libraries Biblioteques de símbols
Symbol Libraries Menu Menú de biblioteques de símbols
Symbol Library Biblioteca de símbols
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -426,6 +426,7 @@
Stroke Weight Tloušťka tahu
Graphic Style Grafický styl
Symbol Instance Instance symbolu
Link Block Series Řada propojených bloků
Font Family Rodina písma
Font Family & Style Rodina a řez písma
Font Family, Style & Size Rodina, řez a velikost písma
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -385,6 +385,7 @@
Stroke Weight Stregtykkelse
Graphic Style Grafisk stil
Symbol Instance Symbolforekomst
Link Block Series Serie af sammenkædede blokke
Font Family Skrifttypefamilie
Font Family & Style Skrifttypefamilie og -stil
Font Family, Style & Size Skrifttypefamilie, stil og størrelse
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3298,6 +3298,7 @@ effect Points Punkte
Graphic Grafik
Show all open documents Alle geöffneten Dokumente anzeigen
Symbol Instance Symbolinstanz
Link Block Series Reihe verketteter Blöcke
Symbol Libraries Symbol-Bibliotheken
Symbol Screener Tool Symbol-transparent-gestalten-Werkzeug
Symbol Scruncher Tool Symbol-stauchen-Werkzeug
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3697,6 +3697,7 @@ digits Malayalam Μαλαγιάλαμ
Graphic Γραφικό
Show all open documents Εμφάνιση όλων των ανοιχτών εγγράφων
Symbol Instance Στιγμιότυπο συμβόλου
Link Block Series Σειρά συνδεδεμένων μπλοκ
Symbol Libraries Βιβλιοθήκες συμβόλων
Symbol Screener Tool Αραίωση συμβόλων (Symbol Screener Tool)
Symbol Scruncher Tool Σύμπτυξη συμβόλων (Symbol Scruncher Tool)
Can't render this file because it contains an unexpected character in line 163 and column 5.
+1
View File
@@ -3354,6 +3354,7 @@ digits Malayalam Malayálam
Graphic Gráfico
Show all open documents Mostrar todos los documentos abiertos
Symbol Instance Instancia de símbolo
Link Block Series Serie de bloques enlazados
Symbol Libraries Bibliotecas de símbolos
Symbol Screener Tool Herramienta Transparencia de símbolos
Symbol Scruncher Tool Herramienta Concentración de símbolos
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -423,6 +423,7 @@
Stroke Weight Viivan paksuus
Graphic Style Grafiikkatyyli
Symbol Instance Symbolin ilmentymä
Link Block Series Linkitettyjen lohkojen sarja
Font Family Fonttiperhe
Font Family & Style Fonttiperhe ja tyyli
Font Family, Style & Size Fonttiperhe, tyyli ja koko
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3346,6 +3346,7 @@ digits Malayalam Malayalam
Graphic Graphique
Show all open documents Afficher tous les documents ouverts
Symbol Instance Instance de symbole
Link Block Series Série de blocs liés
Symbol Libraries Bibliothèques de symboles
Symbol Screener Tool Outil Transparence de symboles
Symbol Scruncher Tool Outil Espacement de symboles
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3988,6 +3988,7 @@ mark-style Japanese Japán
Switch workspace Munkaterület váltása
Symbol Szimbólum
Symbol Instance Szimbólumpéldány
Link Block Series Csatolt blokkok sorozata
Symbol Libraries Szimbólumkönyvtárak
Symbol Screener Tool Szimbólumhalványító
Symbol Scruncher Tool Szimbólumtömörítő
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -385,6 +385,7 @@
Stroke Weight Ketebalan stroke
Graphic Style Gaya grafis
Symbol Instance Instans simbol
Link Block Series Rangkaian blok tertaut
Font Family Keluarga font
Font Family & Style Keluarga & gaya font
Font Family, Style & Size Keluarga, gaya & ukuran font
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
385
386
387
388
389
390
391
+1
View File
@@ -3390,6 +3390,7 @@ digits Malayalam Malayalam
Graphic Grafica
Show all open documents Mostra tutti i documenti aperti
Symbol Instance Istanza di simbolo
Link Block Series Serie di blocchi collegati
Symbol Libraries Librerie simboli
Symbol Screener Tool Strumento Trasparenza simboli
Symbol Scruncher Tool Strumento Addensamento simboli
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -455,6 +455,7 @@ digits Malayalam マラヤーラム数字
Stroke Weight 線幅
Graphic Style グラフィックスタイル
Symbol Instance シンボルインスタンス
Link Block Series 連結ブロックの系列
Font Family フォントファミリー
Font Family & Style フォントファミリー (スタイル)
Font Family, Style & Size フォントファミリー (スタイルとサイズ)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -343,6 +343,7 @@
Stroke Weight კონტურის სისქე
Graphic Style გრაფიკული სტილი
Symbol Instance სიმბოლოს ეგზემპლარი
Link Block Series დაკავშირებული ბლოკების სერია
Font Family შრიფტის ოჯახი
Font Family & Style შრიფტის ოჯახი და სტილი
Font Family, Style & Size შრიფტის ოჯახი, სტილი და ზომა
Can't render this file because it is too large.
+1
View File
@@ -448,6 +448,7 @@ digits Malayalam 말라얄람어
Stroke Weight 선 두께
Graphic Style 그래픽 스타일
Symbol Instance 심볼 인스턴스
Link Block Series 연결된 블록 시리즈
Font Family 글꼴 패밀리
Font Family & Style 글꼴 패밀리 및 스타일
Font Family, Style & Size 글꼴 패밀리, 스타일 및 크기
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -399,6 +399,7 @@
Stroke Weight Contourdikte
Graphic Style Grafische stijl
Symbol Instance Symboolinstantie
Link Block Series Reeks gekoppelde blokken
Font Family Lettertypefamilie
Font Family & Style Lettertypefamilie en -stijl
Font Family, Style & Size Lettertypefamilie, -stijl en -grootte
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -386,6 +386,7 @@
Stroke Weight Grubość obrysu
Graphic Style Styl graficzny
Symbol Instance Wystąpienie symbolu
Link Block Series Seria połączonych bloków
Font Family Rodzina czcionek
Font Family & Style Rodzina i styl czcionki
Font Family, Style & Size Rodzina, styl i rozmiar czcionki
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2757,6 +2757,7 @@ digits Malayalam Malaiala
Switch Units Trocar Unidades
Switch workspace Trocar espaço de trabalho
Symbol Instance Instância de Símbolo
Link Block Series Série de Blocos Vinculados
Symbol Libraries Bibliotecas de Símbolos
Symbol Screener Tool Ferramenta Peneira de Símbolos
Symbol Scruncher Tool Ferramenta Amontoar Símbolos
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -3385,6 +3385,7 @@ digits Malayalam Малаялам
Graphic Графика
Show all open documents Показать все открытые документы
Symbol Instance Экземпляр символа
Link Block Series Серия связанных блоков
Symbol Libraries Библиотеки символов
Symbol Screener Tool Инструмент «Прозрачность символов»
Symbol Scruncher Tool Инструмент «Сжатие символов»
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3276,6 +3276,7 @@ digits Malayalam Malayalam
Graphic Grafik
Show all open documents Visa alla öppna dokument
Symbol Instance Symbolinstans
Link Block Series Serie av länkade block
Symbol Libraries Symbolbibliotek
Symbol Screener Tool Verktyg för symbolopacitet
Symbol Scruncher Tool Verktyg för att dra ihop symboler
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -3169,6 +3169,7 @@ digits Malayalam Malayalam
Graphic Grafik
Show all open documents Tüm açık belgeleri göster
Symbol Instance Sembol Örneği
Link Block Series Bağlantılı Blok Serisi
Symbol Libraries Sembol Kitaplıkları
Symbol Screener Tool Sembol Perdeleyici Aracı
Symbol Scruncher Tool Sembol Ezici Aracı
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -3383,6 +3383,7 @@ effect Points Точки
Graphic Графіка
Show all open documents Показати всі відкриті документи
Symbol Instance Екземпляр символу
Link Block Series Серія пов’язаних блоків
Symbol Libraries Бібліотеки символів
Symbol Screener Tool Інструмент «Прозорість символів»
Symbol Scruncher Tool Інструмент «Стиск символів»
Can't render this file because it contains an unexpected character in line 177 and column 5.
+1
View File
@@ -3706,6 +3706,7 @@ mark-style Japanese Kiểu Nhật
Switch workspace Chuyển không gian làm việc
Switch x/y Đổi x/y
Symbol Instance Thực thể ký hiệu
Link Block Series Chuỗi khối liên kết
Symbol Libraries Thư viện ký hiệu
Symbol Screener Tool Công cụ Lọc ký hiệu
Symbol Scruncher Tool Công cụ Thu gọn ký hiệu
Can't render this file because it contains an unexpected character in line 199 and column 5.
+1
View File
@@ -3222,6 +3222,7 @@ digits Malayalam 马拉雅拉姆数字
Graphic 图形
Show all open documents 显示所有打开的文档
Symbol Instance 符号实例
Link Block Series 链接块系列
Symbol Libraries 符号库
Symbol Screener Tool 符号透明度工具
Symbol Scruncher Tool 符号紧缩工具
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4234,6 +4234,7 @@ digits Malayalam 馬來亞拉姆數字
Graphic 圖形
Show all open documents 顯示所有開啟的文件
Symbol Instance 符號範例
Link Block Series 連結區塊系列
Symbol Libraries 符號資料庫
Symbol Screener Tool 符號濾色器工具
Symbol Scruncher Tool 符號緊縮器工具
Can't render this file because it contains an unexpected character in line 6 and column 47.
+20
View File
@@ -507,6 +507,26 @@ fn update_selection_follows_save_selection_and_needs_a_recall() {
assert_eq!(app.run("select.recall1", json!({})).unwrap()["count"], 2);
}
#[test]
fn select_same_link_block_series_selects_a_thread_from_the_menu() {
let mut app = app_with_a_rectangle();
let entry = menu_entries(&app).into_iter().find(|e| e.command.as_deref() == Some("select.same.linkBlockSeries")).expect("menu entry");
assert_eq!((entry.path, entry.label.as_str()), (vec!["Select".to_string(), "Same".to_string()], "Link Block Series"));
// A rectangle selected: no type, so the item is off.
assert!(!enabled(&app, "select.same.linkBlockSeries"));
let frame = |app: &mut VectorcraftApp, x: i32| {
app.run("text.create", json!({"x": x, "y": 40, "text": "words ".repeat(40), "area": {"width": 60, "height": 30}})).unwrap()["id"].clone()
};
let (a, b) = (frame(&mut app, 0), frame(&mut app, 100));
app.run("select.set", json!({"ids": [a.clone(), b.clone()]})).unwrap();
app.run("text.thread.create", json!({})).unwrap();
app.run("select.set", json!({"ids": [b.clone()]})).unwrap();
assert!(enabled(&app, "select.same.linkBlockSeries"));
invoke(&mut app, "select.same.linkBlockSeries", json!({}));
let ids: Vec<u64> = app.session.active().unwrap().selection.objects.iter().map(|i| i.0).collect();
assert_eq!(json!(ids), json!([a, b]));
}
#[test]
fn edit_selection_opens_its_dialog_on_the_saved_selections() {
let mut app = app_with_a_rectangle();
+1
View File
@@ -462,6 +462,7 @@ pub fn menu_tree_named(english_names: bool) -> Vec<(&'static str, Vec<Item>)> {
c("Graphic Style", "select.same.graphicStyle"),
c("Shape", "select.same.shapeType"),
c("Symbol Instance", "select.same.symbolInstance"),
c("Link Block Series", "select.same.linkBlockSeries"),
Sep,
c("Font Family", "select.same.fontFamily"),
c("Font Family & Style", "select.same.fontFamilyStyle"),
+7
View File
@@ -4883,6 +4883,13 @@ after it (`out`). The whole story flows into the frames before the break; the fr
threaded with each other when two or more remain, and the first of them starts a paragraph. It refuses an `id` that
doesn't exist, a `port` other than `in` or `out`, and a side where no frame of a thread comes before or after `id`.
`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. Type that is hidden or locked, inside a hidden or locked group or layer, on a template layer, or outside
the isolated group does not count as selected, and such frames of a thread are left out. It is off while no other 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,
and it has no bullet or number. In a frame that is no longer threaded (after `text.thread.releaseSelection` or
+1
View File
@@ -888,6 +888,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Type › Convert To Point Type on a frame of threaded text makes point type of the text that frame holds and takes it out of the thread; the story flows through the other frames without that text, so each character is in the document once.
Object › Rasterize on a frame of threaded text takes it out of the thread with the text it shows, which the image draws, and the other frames keep the text they hold. When the last frame is rasterized, the text it has no room for goes after the text of the last frame that stays.
Type › Create Outlines, Object › Expand and Object › Expand Appearance on a frame of threaded text take it out of the thread with the text it shows, which the outlines draw, and the other frames keep the text they hold. When the last frame is outlined, the text it has no room for goes after the text of the last frame that stays. When Expand Appearance keeps the frame as type in a group with its shadow, outer glow or Crop Marks, the type stays in the thread. The public help on threads ([Create text threads between objects](https://helpx.adobe.com/illustrator/desktop/design-with-text/add-manage-text/create-text-threads-between-objects.html), [Remove or break text threads between objects](https://helpx.adobe.com/illustrator/desktop/design-with-text/add-manage-text/remove-or-break-text-threads-between-objects.html)) does not describe outlining a threaded frame; this handling is VectorCraft's own behavior.
Select › Same › Link Block Series selects every frame of the threads of the selected type; type in no thread selects itself (`select.same.linkBlockSeries`). The menu entry is listed in [Select objects by characteristics](https://helpx.adobe.com/illustrator/desktop/manage-objects/select-objects/select-objects-by-characteristics.html); selecting several threads at once and type in no thread selecting itself are VectorCraft's own behavior.
Object › Flatten Transparency takes a frame of threaded text out of the thread in the same way when Convert All Text to Outlines outlines it or when the frame becomes flat regions or an image, and the other frames keep the text they hold.
New Symbol and Redefine Symbol on a frame of threaded text take it out of the thread with the text it shows, which the symbol's art holds, and the other frames keep the text they hold. When the last frame goes into the symbol, the text it has no room for goes after the text of the last frame that stays. When every frame of a thread goes into the symbol, the copy of the last frame keeps all its text.
With Optical Margin Alignment on, or Roman Hanging Punctuation on in the first paragraph, the glyphs at the ends of left-to-right type on a path hang outside its brackets, measured along the path. The type hangs at the start bracket when its line is placed there, hangs at the end bracket when the line is placed there, and hangs at both when it is centered. Glyphs that hang past the end bracket do not make the type overflow, and a hang stops at an open path's end. SVG export writes such type as outlines.