Merge pull request #4418 from dotCooCoo/feat/hide-unselected-shortcut

Object: Cmd/Ctrl+Alt+Shift+3 hides every unselected object
This commit is contained in:
BFlat
2026-10-29 15:40:35 +07:00
committed by GitHub
31 changed files with 232 additions and 4 deletions
+50 -1
View File
@@ -13,7 +13,7 @@ use vectorcraft_geom::{Affine, Anchor, PathData, Point, Rect, Vec2};
use vectorcraft_render::AntiAlias;
use vectorcraft_render::encode::{RasterExportOptions, RasterFormat};
use super::edit::{duplicate_in, selected_roots};
use super::edit::{duplicate_in, selected_roots, selected_roots_to_transform};
use super::{
CommandSpec, EngineError, Result, Session, bad, check_numbers, check_switches, cmd, f64_or, fileio, find_command, has_doc, has_selection,
has_selection_outside_isolation, switch_or,
@@ -74,6 +74,15 @@ pub fn specs() -> Vec<CommandSpec> {
has_selection_outside_isolation,
|s, _| other_layers(s, false)
),
cmd!(
"object.hide.unselected",
"Unselected Artwork",
["Object", "Hide"],
Some("Cmd+Alt+Shift+3"),
"{} hide, in one undo step, every object select.all takes that is not selected and holds no selected object; in a group that holds one it hides the other members one by one, and a clip group, compound path or other object that holds one stays as it is; guide paths stay as they are while Lock Guides is on; object.showAll shows them again; disabled in isolation mode → {count}",
has_selection_outside_isolation,
hide_unselected
),
cmd!(
"object.transformEach",
"Transform Each…",
@@ -312,6 +321,46 @@ fn other_layers(s: &mut Session, lock: bool) -> Result<Value> {
Ok(json!({ "count": n }))
}
/// The objects [`super::select::walk_selectable`] visits that are not among the selected objects `roots` and hold none of
/// them. The walk goes into a plain group that holds one of `roots`; a clip group, a Shaper group, a compound path and
/// every other object that holds one is left out with its members. Guide paths are left out while `guides_locked`.
fn unselected(d: &vectorcraft_doc::Document, iso: Option<NodeId>, roots: &[NodeId], guides_locked: bool) -> Vec<NodeId> {
let holds: HashSet<NodeId> = d.index_paths(roots.iter().copied()).values().filter_map(|p| d.ancestry_at(p)).flatten().collect();
let roots: HashSet<NodeId> = roots.iter().copied().collect();
let mut out = vec![];
super::select::walk_selectable(d, iso, |n| {
if !holds.contains(&n.id) {
if !(guides_locked && matches!(n.kind, NodeKind::Path { guide: true, .. })) {
out.push(n.id);
}
return false;
}
!roots.contains(&n.id) && matches!(n.kind, NodeKind::Group { clip: false, .. }) && n.extras().shaper.is_none()
});
out
}
fn hide_unselected(s: &mut Session, _: &Value) -> Result<Value> {
let roots = selected_roots_to_transform(s)?;
let guides_locked = s.menu.guides_locked;
let st = s.doc()?;
let ids = unselected(&st.doc, st.isolation, &roots, guides_locked);
// With nothing to hide, the document and its undo history stay as they are.
if !ids.is_empty() {
s.edit("Hide", |d, _| {
// Hiding moves no node in the tree, so the paths found up front stay right.
let paths = d.index_paths(ids.iter().copied());
for id in &ids {
if let Some(n) = paths.get(id).and_then(|p| d.node_mut_at(p)) {
n.visible = false;
}
}
Ok(())
})?;
}
Ok(json!({ "count": ids.len() }))
}
// ---------- Transform Each ----------
/// Small deterministic PRNG (xorshift64*) for Transform Each → Random.
+106
View File
@@ -492,6 +492,112 @@ fn lock_and_hide_with_nothing_to_lock_or_hide_record_no_undo_step() {
}
}
/// Hide › Unselected Artwork hides, in one undo step, each object that is not selected and holds no selected object:
/// here `b`, the member `n` of the group that holds the selected `m`, and `p` on another layer. The selected objects,
/// that group, the clip group that holds the selected `c2` with all its members, a locked object, the art on a locked
/// layer and an object hidden before stay as they are, and so does the selection. Show All shows them again.
#[test]
fn hide_unselected_artwork_hides_every_object_that_is_not_selected() {
let mut s = session();
let a = rect(&mut s, 0.0, 0.0, 10.0, 10.0);
let b = rect(&mut s, 20.0, 0.0, 10.0, 10.0);
let m = rect(&mut s, 40.0, 0.0, 10.0, 10.0);
let n = rect(&mut s, 60.0, 0.0, 10.0, 10.0);
sel(&mut s, &[m, n]);
let g = id_of(&s.execute("object.group", &json!({})).unwrap());
let c1 = rect(&mut s, 80.0, 0.0, 10.0, 10.0);
let c2 = rect(&mut s, 85.0, 5.0, 10.0, 10.0);
let c3 = rect(&mut s, 80.0, 0.0, 30.0, 30.0);
sel(&mut s, &[c1, c2, c3]);
let clip = id_of(&s.execute("object.clippingMask.make", &json!({})).unwrap());
let k = rect(&mut s, 120.0, 0.0, 10.0, 10.0);
s.execute("layer.setProps", &json!({"id": k.0, "locked": true})).unwrap();
let h = rect(&mut s, 140.0, 0.0, 10.0, 10.0);
s.execute("layer.setProps", &json!({"id": h.0, "visible": false})).unwrap();
s.execute("layer.new", &json!({})).unwrap();
let p = rect(&mut s, 0.0, 100.0, 10.0, 10.0);
let locked_layer = id_of(&s.execute("layer.new", &json!({})).unwrap());
let q = rect(&mut s, 20.0, 100.0, 10.0, 10.0);
s.execute("layer.setProps", &json!({"id": locked_layer.0, "locked": true})).unwrap();
let shown = |s: &Session, ids: &[NodeId]| ids.iter().map(|id| node(s, *id).visible).collect::<Vec<_>>();
sel(&mut s, &[a, m, c2]);
let steps = undo_len(&s);
assert_eq!(s.execute("object.hide.unselected", &json!({})).unwrap(), json!({"count": 3}));
assert_eq!(shown(&s, &[b, n, p, h]), [false; 4]);
assert_eq!(shown(&s, &[a, m, g, c1, c2, c3, clip, k, q]), [true; 9]);
assert!(node(&s, k).locked);
assert_eq!((undo_len(&s), selected(&s)), (steps + 1, vec![a, m, c2]));
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(shown(&s, &[b, n, p]), [true; 3]);
s.execute("edit.redo", &json!({})).unwrap();
s.execute("object.showAll", &json!({})).unwrap();
assert_eq!(shown(&s, &[b, n, p, h]), [true; 4]);
}
/// Hide › Unselected Artwork leaves the art on a locked artboard as it is, as Select All leaves it unselected.
#[test]
fn hide_unselected_artwork_leaves_the_art_on_a_locked_artboard() {
let mut s = session();
let (a, b) = (rect(&mut s, 0.0, 0.0, 10.0, 10.0), rect(&mut s, 20.0, 0.0, 10.0, 10.0));
let on_locked = rect(&mut s, 2050.0, 50.0, 10.0, 10.0);
s.execute("artboard.new", &json!({"x": 2000, "y": 0, "width": 400, "height": 400})).unwrap();
s.execute("artboard.setProps", &json!({"index": 1, "locked": true})).unwrap();
sel(&mut s, &[a]);
assert_eq!(s.execute("object.hide.unselected", &json!({})).unwrap(), json!({"count": 1}));
assert_eq!([a, b, on_locked].map(|id| node(&s, id).visible), [true, false, true]);
}
/// In isolation mode Hide › Unselected Artwork is disabled: the menu item is off, and the command returns an error that
/// names isolation mode and changes nothing. While Lock Guides is on it leaves the guides as they are. With nothing
/// left to hide it records no undo step and returns a count of 0. A compound path that holds a selected path stays as
/// it is.
#[test]
fn hide_unselected_artwork_keeps_locked_guides_and_is_disabled_in_isolation_mode() {
let mut s = session();
let guide = rect(&mut s, 0.0, 0.0, 10.0, 10.0);
sel(&mut s, &[guide]);
s.execute("view.guides.make", &json!({})).unwrap();
s.execute("view.guides.lock", &json!({"locked": true})).unwrap();
let out = rect(&mut s, 20.0, 0.0, 10.0, 10.0);
let m = rect(&mut s, 40.0, 0.0, 10.0, 10.0);
let n = rect(&mut s, 60.0, 0.0, 10.0, 10.0);
sel(&mut s, &[m, n]);
let g = id_of(&s.execute("object.group", &json!({})).unwrap());
let hide = |s: &mut Session| s.execute("object.hide.unselected", &json!({})).unwrap();
let shown = |s: &Session, ids: &[NodeId]| ids.iter().map(|id| node(s, *id).visible).collect::<Vec<_>>();
s.execute("object.isolate", &json!({"id": g.0})).unwrap();
select_with_a_step_to_redo(&mut s, &[m]);
let before = history(&s);
let info = find_command("object.hide.unselected").unwrap().info(&s);
assert!(!info.enabled && info.disabled_reason.as_deref() == Some("exit isolation mode first"), "{info:?}");
let e = s.execute("object.hide.unselected", &json!({})).unwrap_err();
assert!(matches!(e, EngineError::Disabled(..)) && e.to_string().contains("isolation mode"), "{e}");
assert_eq!(shown(&s, &[m, n, out, guide]), [true; 4]);
assert_eq!((history(&s), selected(&s)), (before, vec![m]));
s.execute("object.exitIsolation", &json!({})).unwrap();
sel(&mut s, &[m]);
assert!(find_command("object.hide.unselected").unwrap().info(&s).enabled);
assert_eq!(hide(&mut s), json!({"count": 2}));
assert_eq!(shown(&s, &[n, out, guide]), [false, false, true]);
select_with_a_step_to_redo(&mut s, &[m]);
let before = history(&s);
assert_eq!(hide(&mut s), json!({"count": 0}));
assert_eq!(history(&s), before);
// With Lock Guides off, the guide path is hidden like other art.
s.execute("view.guides.lock", &json!({"locked": false})).unwrap();
sel(&mut s, &[m]);
assert_eq!(hide(&mut s), json!({"count": 1}));
assert_eq!(shown(&s, &[guide]), [false]);
// A compound path that holds the selected path stays as it is with its paths.
let outer = rect(&mut s, 0.0, 200.0, 100.0, 100.0);
let hole = rect(&mut s, 40.0, 240.0, 20.0, 20.0);
sel(&mut s, &[outer, hole]);
let c = id_of(&s.execute("object.compoundPath.make", &json!({})).unwrap());
sel(&mut s, &[hole]);
assert_eq!(hide(&mut s), json!({"count": 1}));
assert_eq!(shown(&s, &[g, c, outer, hole]), [false, true, true, true]);
}
// ---------- Transform Each ----------
#[test]
+1
View File
@@ -3396,6 +3396,7 @@ font-technology Variable Променливи
Unlock Guides Отключване на водачите
Unnamed Objects Безименни обекти
Unprintable margin: Непечатаемо поле:
Unselected Artwork Неизбрани графични елементи
Unshift Преместване назад (Unshift)
Untitled Без заглавие
Update Link Актуализиране на връзката
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -191,6 +191,7 @@
Other Layers Ostatní vrstvy
Unlock All Odemknout vše
Hide Skrýt
Unselected Artwork Nevybranou kresbu
Show All Zobrazit vše
Expand… Rozložit…
Expand Appearance Rozložit vzhled
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -176,6 +176,7 @@
Other Layers Andre lag
Unlock All Lås alle op
Hide Skjul
Unselected Artwork Ikke-markeret grafik
Show All Vis alle
Expand… Udvid…
Expand Appearance Udvid udseende
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3414,6 +3414,7 @@ effect Points Punkte
Unlock Guides Hilfslinien entsperren
Unnamed Objects Unbenannte Objekte
Unprintable margin: Nicht druckbarer Rand:
Unselected Artwork Nicht ausgewählte Grafik
Unshift Verschiebung aufheben
Untitled Unbenannt
Update Link Verknüpfung aktualisieren
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3750,6 +3750,7 @@ theme Appearance Εμφάνιση
Unlock Guides Ξεκλείδωμα οδηγών
Unnamed Objects Αντικείμενα χωρίς όνομα
Unprintable margin: Μη εκτυπώσιμο περιθώριο:
Unselected Artwork Μη επιλεγμένα γραφικά στοιχεία
Unshift Αναίρεση μετατόπισης
Update Link Ενημέρωση συνδέσμου
Update Links Ενημέρωση συνδέσμων
Can't render this file because it contains an unexpected character in line 2071 and column 19.
+1
View File
@@ -3464,6 +3464,7 @@ font-technology Variable Variables
Unlock Guides Desbloquear guías
Unnamed Objects Objetos sin nombre
Unprintable margin: Margen no imprimible:
Unselected Artwork Ilustración no seleccionada
Unshift Deshacer cambio
Untitled Sin título
Update Link Actualizar vínculo
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -198,6 +198,7 @@
Other Layers Muut tasot
Unlock All Avaa kaikki lukitukset
Hide Piilota
Unselected Artwork Valitsematon kuvitus
Show All Näytä kaikki
Expand… Laajenna…
Expand Appearance Laajenna ulkoasu
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3461,6 +3461,7 @@ font-technology Variable Variables
Unlock Guides Déverrouiller les repères
Unnamed Objects Objets sans nom
Unprintable margin: Marge non imprimable :
Unselected Artwork Illustration non sélectionnée
Unshift Annuler le décalage
Untitled Sans titre
Update Link Mettre à jour le lien
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4169,6 +4169,7 @@ mark-style Japanese Japán
Unlock Variables Változók zárolásának feloldása
Unnamed Objects Névtelen objektumok
Unprintable margin: Nem nyomtatható margó:
Unselected Artwork A nem kijelölt grafika
Unsharp Mask Életlenítő maszk
Unsharp Mask… Életlenítő maszk…
Unshift Eltolás visszavonása
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -176,6 +176,7 @@
Other Layers Lapisan lain
Unlock All Buka kunci semua
Hide Sembunyikan
Unselected Artwork Karya seni yang tidak dipilih
Show All Tampilkan semua
Expand… Perluas…
Expand Appearance Perluas tampilan
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
176
177
178
179
180
181
182
+1
View File
@@ -3499,6 +3499,7 @@ mark-style Japanese Giapponese
Unlock Guides Sblocca guide
Unnamed Objects Oggetti senza nome
Unprintable margin: Margine non stampabile:
Unselected Artwork Grafica non selezionata
Unshift Annulla spostamento
Untitled Senza titolo
Update Link Aggiorna collegamento
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -196,6 +196,7 @@ font-technology Variable バリアブル
Other Layers その他のレイヤー
Unlock All すべてをロック解除
Hide 隠す
Unselected Artwork 選択されていないアートワーク
Show All すべてを表示
Expand… 分割・拡張…
Expand Appearance アピアランスを分割
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -142,6 +142,7 @@
Other Layers სხვა შრეები
Unlock All ყველაფრის განბლოკვა
Hide დამალვა
Unselected Artwork მოუნიშნავი ნახატი
Show All ყველას ჩვენება
Expand… გაშლა…
Expand Appearance იერსახის გაშლა
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -192,6 +192,7 @@ font-technology Variable 가변
Other Layers 다른 레이어
Unlock All 모두 잠금 해제
Hide 숨기기
Unselected Artwork 선택되지 않은 아트워크
Show All 모두 표시
Expand… 확장…
Expand Appearance 모양새 확장
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -190,6 +190,7 @@
Other Layers Andere lagen
Unlock All Alles ontgrendelen
Hide Verbergen
Unselected Artwork Niet-geselecteerde artwork
Show All Alles tonen
Expand… Uitbreiden…
Expand Appearance Uiterlijk uitbreiden
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -156,6 +156,7 @@
Other Layers Pozostałe warstwy
Unlock All Odblokuj wszystko
Hide Ukryj
Unselected Artwork Niezaznaczoną kompozycję
Show All Pokaż wszystko
Expand… Rozszerz…
Expand Appearance Rozszerz wygląd
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2827,6 +2827,7 @@
Unlock Guides Desbloquear Guias
Unnamed Objects Objetos Sem Nome
Unprintable margin: Margem não imprimível:
Unselected Artwork Arte Não Selecionada
Unshift Desfazer Mudança de Cor
Update Link Atualizar Link
Update Links Atualizar Links
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -3495,6 +3495,7 @@ mark-style Japanese Японский
Unlock Guides Разблокировать направляющие
Unnamed Objects Объекты без имени
Unprintable margin: Непечатаемое поле:
Unselected Artwork Невыделенный рисунок
Unshift Отменить сдвиг
Untitled Без названия
Update Link Обновить связь
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3396,6 +3396,7 @@ font-technology Variable Variabla
Unlock Guides Lås upp stödlinjer
Unnamed Objects Namnlösa objekt
Unprintable margin: Ej utskrivbar marginal:
Unselected Artwork Omarkerad grafik
Unshift Utan förskjutning
Untitled Namnlös
Update Link Uppdatera länk
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -3288,6 +3288,7 @@ font-technology Variable Değişken
Unlock Guides Kılavuzların Kilidini Aç
Unnamed Objects Adsız Nesneler
Unprintable margin: Yazdırılamayan kenar boşluğu:
Unselected Artwork Seçili Olmayan Sanat Eserleri
Unshift Kaydırmayı kaldır
Untitled İsimsiz
Update Link Bağlantıyı Güncelle
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -3492,6 +3492,7 @@ effect Points Точки
Unlock Guides Розблокувати напрямні
Unnamed Objects Об’єкти без імені
Unprintable margin: Недруковане поле:
Unselected Artwork Невиділений малюнок
Unshift Скасувати зсув
Untitled Без назви
Update Link Оновити зв’язок
Can't render this file because it contains an unexpected character in line 749 and column 40.
+1
View File
@@ -3861,6 +3861,7 @@ mark-style Japanese Kiểu Nhật
Unlock Variables Mở khóa biến số
Unnamed Objects Đối tượng chưa đặt tên
Unprintable margin: Lề không in được:
Unselected Artwork Tác phẩm không được chọn
Unsharp Mask Tăng độ sắc nét
Unsharp Mask… Tăng độ sắc nét…
Unshift Bỏ dịch chuyển
1 # Vietnamese (vi) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
3861
3862
3863
3864
3865
3866
3867
+1
View File
@@ -3332,6 +3332,7 @@ font-script Japanese 汉字
Unlock Guides 解锁参考线
Unnamed Objects 未命名对象
Unprintable margin: 不可打印边界:
Unselected Artwork 未选择的图稿
Unshift 取消偏移
Untitled 未命名
Update Link 更新链接
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4286,6 +4286,7 @@ font-script Japanese 漢字
Unlock Guides 解除鎖定參考線
Unnamed Objects 未命名物件
Unprintable margin: 不可列印邊界:
Unselected Artwork 未選取的圖稿
Unshift 取消位移
Untitled 未命名
Update Link 更新連結
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3 -2
View File
@@ -683,6 +683,7 @@ fn lock_and_hide_menus_are_greyed_out_in_isolation_mode() {
"object.hide",
"object.hide.above",
"object.hide.otherLayers",
"object.hide.unselected",
"object.showAll"
]
);
@@ -696,10 +697,10 @@ fn lock_and_hide_menus_are_greyed_out_in_isolation_mode() {
};
app.run("object.isolate", json!({"id": g.clone()})).unwrap();
app.run("select.set", json!({"ids": [b]})).unwrap();
assert_eq!(shown(&app), [false, false, false, false, false, false, false, false, true], "in isolation mode");
assert_eq!(shown(&app), [false, false, false, false, false, false, false, false, false, true], "in isolation mode");
app.run("object.exitIsolation", json!({})).unwrap();
app.run("select.set", json!({"ids": [g]})).unwrap();
assert_eq!(shown(&app), [true; 9], "after Exit Isolation Mode");
assert_eq!(shown(&app), [true; 10], "after Exit Isolation Mode");
}
#[test]
+6 -1
View File
@@ -170,7 +170,12 @@ pub fn menu_tree_named(english_names: bool) -> Vec<(&'static str, Vec<Item>)> {
c("Unlock All", "object.unlockAll"),
sub(
"Hide",
vec![c("Selection", "object.hide"), c("All Artwork Above", "object.hide.above"), c("Other Layers", "object.hide.otherLayers")],
vec![
c("Selection", "object.hide"),
c("All Artwork Above", "object.hide.above"),
c("Other Layers", "object.hide.otherLayers"),
c("Unselected Artwork", "object.hide.unselected"),
],
),
c("Show All", "object.showAll"),
Sep,
+34
View File
@@ -200,6 +200,40 @@ fn cmd_shift_o_creates_outlines() {
assert!(app.ui.dialog.is_none(), "not File › Open while typing");
}
/// Cmd+Alt+Shift+3 is Object › Hide › Unselected Artwork: it hides the object that is not
/// selected. Cmd+Alt+3 still shows all, and Cmd+3 still hides the selection. In isolation mode
/// the item is disabled.
#[test]
fn cmd_alt_shift_3_hides_unselected_artwork() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
assert_eq!(crate::menus::shortcut_of("object.hide.unselected"), Some("Cmd+Alt+Shift+3"));
let rect = |app: &mut VectorcraftApp, x: f64| {
let r = app.session.execute("shape.rectangle", &json!({"x": x, "y": 10, "width": 20, "height": 20})).unwrap();
vectorcraft_doc::NodeId(r["id"].as_u64().unwrap())
};
let (a, b) = (rect(&mut app, 10.0), rect(&mut app, 50.0));
let shown = |app: &VectorcraftApp| [a, b].map(|id| app.session.doc().unwrap().doc.node(id).unwrap().visible);
let three = |modifiers| egui::Event::Key { key: Key::Num3, physical_key: None, pressed: true, repeat: false, modifiers };
app.session.execute("select.set", &json!({"ids": [a.0]})).unwrap();
frame(&mut app, vec![three(Modifiers::COMMAND | Modifiers::ALT | Modifiers::SHIFT)]);
assert_eq!(shown(&app), [true, false]);
frame(&mut app, vec![three(Modifiers::COMMAND | Modifiers::ALT)]);
assert_eq!(shown(&app), [true, true], "Cmd+Alt+3 shows all");
app.session.execute("select.set", &json!({"ids": [a.0]})).unwrap();
frame(&mut app, vec![three(Modifiers::COMMAND)]);
assert_eq!(shown(&app), [false, true], "Cmd+3 hides the selection");
// In isolation mode the menu item is off, and the shortcut hides nothing.
app.session.execute("object.showAll", &json!({})).unwrap();
app.session.execute("select.set", &json!({"ids": [a.0, b.0]})).unwrap();
let g = app.session.execute("object.group", &json!({})).unwrap()["id"].as_u64().unwrap();
app.session.execute("object.isolate", &json!({"id": g})).unwrap();
app.session.execute("select.set", &json!({"ids": [a.0]})).unwrap();
assert!(!crate::menus::enabled(&app, "object.hide.unselected"));
frame(&mut app, vec![three(Modifiers::COMMAND | Modifiers::ALT | Modifiers::SHIFT)]);
assert_eq!(shown(&app), [true, true], "nothing is hidden in isolation mode");
}
/// Cmd+Shift+B while the Type tool edits text is Type › Bold, not Hide Bounding Box (#724).
#[test]
fn cmd_shift_b_while_typing_is_bold_not_the_bounding_box() {
+8
View File
@@ -1369,6 +1369,14 @@ and `dataset.prev` also deselect every selected object that is hidden or locked
moved into a hidden or locked layer or group, or objects that a data set hides. The key object is cleared when it
is one of them, and `edit.undo` selects them again with the key object.
`object.hide.unselected` (Object › Hide › Unselected Artwork, Cmd+Alt+Shift+3) hides, in one undo step, every
object that `select.all` takes and that is neither selected nor holds a selected object, and returns `{count}`. In a
group that holds a selected object it hides the group's other members one by one; a clip group, a compound path or
another object that holds a selected object stays as it is with its members. While Lock Guides is on it leaves the
guide paths as they are. The selection stays as it is, and `object.showAll` shows the objects again. With nothing to
hide it records no undo step. In isolation mode the menu item is disabled, and the command returns the error "exit
isolation mode first" and changes nothing.
These Select menu commands select only the objects that `select.all` selects and the objects inside them: every
`select.same.*` and `select.object.*` command except `select.object.slices`, `select.nextAbove`, `select.nextBelow`
and `select.recall`. They leave out hidden and locked objects with the objects inside them, the objects on hidden,
+1
View File
@@ -921,6 +921,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Neutral wording:** labels, MCP tool text, docs and packaging use neutral names, and `cargo xtask brands` (part of `cargo xtask ci`) fails on vendor names.
- **Colour, type and file workflows:** Recolor Artwork (dialog with harmonies), Edit Colors, Find & Replace, Change Case, Smart Punctuation, Guides, Lock/Hide Above, Transform Each, Rasterize.
Object › Lock › All Artwork Above and Object › Hide › All Artwork Above deselect the selected objects they lock or hide, the key object included.
Object › Hide › Unselected Artwork (Cmd/Ctrl+Alt+Shift+3, `object.hide.unselected`) hides, in one undo step, every object Select All takes that is not selected and holds no selected object; the members of a group that holds a selected object are hidden one by one, and Object › Show All shows them again. It is disabled in isolation mode. Public references: [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html), "Hide unselected artwork"; [How to lock, hide, and delete objects in Illustrator](https://web.archive.org/web/20220303070637/https://helpx.adobe.com/illustrator/using/locking-hiding-deleting-objects.html), which says the Show All and Hide menus are unavailable in isolation mode.
In isolation mode, the items of Object › Lock and Object › Hide, Unlock All and Show All are greyed out ([lock, hide, and delete objects](https://web.archive.org/web/20220303070637/https://helpx.adobe.com/illustrator/using/locking-hiding-deleting-objects.html)); the Contextual Task Bar's Lock button is greyed out too, the Properties panel's Quick Actions have no Lock, and the commands return an error. Pattern editing mode and opacity mask editing do not count as isolation mode for them.
Liquify (its brush Width, Height and Angle, Intensity, Detail, Simplify, Twirl Rate, Complexity, Horizontal and Vertical), Puppet Warp (its Expand and each pin's angle), Transform in Perspective (its depth), Nudge in Perspective (its direction) and Move Plane (its location or distance) refuse a value other than a number or null, such as "40", Draw in Perspective refuses a click point that is not two numbers, and Width Profile refuses a point that is not three numbers; each leaves the document and its undo history as they were.
Object › Lock › All Artwork Above and Object › Hide › All Artwork Above go through each top-level layer once, checking each object that is not selected against the selected objects below it: with 100 tiles selected under 25,000 rectangles, Lock took 6.4 s and now takes under 0.1 s, and with all of 5,001 objects selected it took 8.8 s and now takes under 0.1 s.