mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #2483 from dotCooCoo/feat/layers-save-selection-menu
Layers: the panel menu's Save Selection submenu saves, lists and updates saved selections
This commit is contained in:
@@ -2537,6 +2537,7 @@ mark-style Japanese Japanisch
|
||||
Save Recovery Data Wiederherstellungsdaten speichern
|
||||
Save Save for Web Preset Vorgabe für „Für Web speichern“ speichern
|
||||
Save selected colors as swatches Ausgewählte Farben als Farbfelder speichern
|
||||
Save Selection Auswahl speichern
|
||||
Save Selection… Auswahl speichern…
|
||||
Update Selection Auswahl aktualisieren
|
||||
Save Swatch Library Farbfeldbibliothek speichern
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2588,6 +2588,7 @@ mark-style Japanese Giapponese
|
||||
Save Recovery Data Salva dati di recupero
|
||||
Save Save for Web Preset Salva preset di Salva per il web
|
||||
Save selected colors as swatches Salva i colori selezionati come campioni
|
||||
Save Selection Salva selezione
|
||||
Save Selection… Salva selezione…
|
||||
Update Selection Aggiorna selezione
|
||||
Save Swatch Library Salva libreria campioni
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -396,6 +396,7 @@
|
||||
Point Text Objects ポイント文字オブジェクト
|
||||
Area Text Objects エリア内文字オブジェクト
|
||||
Start Global Edit グローバル編集を開始
|
||||
Save Selection 選択範囲を保存
|
||||
Save Selection… 選択範囲を保存…
|
||||
Update Selection 選択範囲を更新
|
||||
Edit Selection… 選択範囲を編集…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -392,6 +392,7 @@
|
||||
Point Text Objects 포인트 문자 개체
|
||||
Area Text Objects 영역 문자 개체
|
||||
Start Global Edit 전체 편집 시작
|
||||
Save Selection 선택 영역 저장
|
||||
Save Selection… 선택 영역 저장…
|
||||
Update Selection 선택 영역 업데이트
|
||||
Edit Selection… 선택 영역 편집…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2584,6 +2584,7 @@ mark-style Japanese Японский
|
||||
Save Recovery Data Сохранить данные восстановления
|
||||
Save Save for Web Preset Сохранить набор «Сохранить для web»
|
||||
Save selected colors as swatches Сохранить выбранные цвета как образцы
|
||||
Save Selection Сохранить выделение
|
||||
Save Selection… Сохранить выделение…
|
||||
Update Selection Обновить выделение
|
||||
Save Swatch Library Сохранить библиотеку образцов
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2580,6 +2580,7 @@ mark-style Japanese Японський
|
||||
Save Recovery Data Зберегти дані відновлення
|
||||
Save Save for Web Preset Зберегти набір «Зберегти для Інтернету»
|
||||
Save selected colors as swatches Зберегти вибрані кольори як зразки
|
||||
Save Selection Зберегти виділення
|
||||
Save Selection… Зберегти виділення…
|
||||
Update Selection Оновити виділення
|
||||
Save Swatch Library Зберегти бібліотеку зразків
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 721 and column 40.
|
@@ -2873,6 +2873,7 @@ mark-style Japanese Kiểu Nhật
|
||||
Save Recovery Data Lưu dữ liệu khôi phục
|
||||
Save Save for Web Preset Lưu thiết lập sẵn Lưu cho Web
|
||||
Save selected colors as swatches Lưu màu đã chọn thành mẫu màu
|
||||
Save Selection Lưu vùng chọn
|
||||
Save Selection… Lưu vùng chọn…
|
||||
Update Selection Cập nhật vùng chọn
|
||||
Save Swatch Library Lưu thư viện mẫu màu
|
||||
|
||||
|
@@ -2463,6 +2463,7 @@ mark-style Japanese 日式
|
||||
Save Recovery Data 保存恢复数据
|
||||
Save Save for Web Preset 保存 Web 预设
|
||||
Save selected colors as swatches 将所选颜色存储为色板
|
||||
Save Selection 保存选区
|
||||
Save Selection… 保存选区…
|
||||
Update Selection 更新选区
|
||||
Save Swatch Library 存储色板库
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2562,6 +2562,7 @@ mark-style Japanese 日式
|
||||
Save Recovery Data 儲存救援資料
|
||||
Save Save for Web Preset 儲存「儲存為網頁用」預設集
|
||||
Save selected colors as swatches 將所選顏色儲存為色票
|
||||
Save Selection 儲存選取範圍
|
||||
Save Selection… 儲存選取範圍…
|
||||
Update Selection 更新選取範圍
|
||||
Save Swatch Library 儲存色票資料庫
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1497,6 +1497,8 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
items.extend([
|
||||
None,
|
||||
Some(("Locate Object", "layer.locate", json!({}), has_sel, false)),
|
||||
// Sentinel: rendered as the Save Selection submenu (the Select menu's saved-selection items).
|
||||
Some(("Save Selection", "", json!({}), true, false)),
|
||||
None,
|
||||
Some(("Merge Selected", "layer.merge", json!({}), rows.len() >= 2, false)),
|
||||
Some(("Flatten Artwork", "layer.flatten", json!({}), true, false)),
|
||||
@@ -1551,6 +1553,7 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
app.run("assets.add", p).ok();
|
||||
});
|
||||
}
|
||||
Some(("Save Selection", "", ..)) => save_selection_menu(app, ui),
|
||||
Some((label, cmd, p, enabled, checked)) => {
|
||||
if widgets::menu_item(ui, label, enabled, checked) {
|
||||
app.run(cmd, p).ok();
|
||||
@@ -1577,6 +1580,29 @@ fn collect_for_export_menu(ui: &mut Ui, enabled: bool, mut run: impl FnMut(bool)
|
||||
});
|
||||
}
|
||||
|
||||
/// Save Selection › Save Selection…, Update Selection and Edit Selection…, enabled and run as in
|
||||
/// the Select menu, then the active document's saved selections: choosing one recalls it.
|
||||
fn save_selection_menu(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
ui.menu_button(format!(" {}", tl!("Save Selection")), |ui| {
|
||||
for (label, id) in [("Save Selection…", "select.save"), ("Update Selection", "select.update"), ("Edit Selection…", "select.editSaved")] {
|
||||
if widgets::menu_item(ui, label, crate::menus::enabled(app, id), false) {
|
||||
crate::menus::invoke(app, id, json!({}));
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
let names: Vec<String> = app.session.active().map(|d| d.doc.saved_selections.iter().map(|s| s.name.clone()).collect()).unwrap_or_default();
|
||||
if !names.is_empty() {
|
||||
ui.separator();
|
||||
}
|
||||
for name in names {
|
||||
if widgets::menu_item_name(ui, &name, true, false) {
|
||||
app.run("select.recall", json!({ "name": name })).ok();
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// `ui.layersExpand {ids?, open?}`: open (or close) rows of the active document's Layers panel;
|
||||
/// without `ids`, every row that holds others.
|
||||
pub fn expand(app: &mut VectorcraftApp, p: &Value) -> Result<Value, String> {
|
||||
@@ -2540,6 +2566,7 @@ mod tests {
|
||||
"Make/Release Clipping Mask",
|
||||
"Enter Isolation Mode",
|
||||
"Locate Object",
|
||||
"Save Selection",
|
||||
"Merge Selected",
|
||||
"Flatten Artwork",
|
||||
"Collect in New Layer",
|
||||
@@ -2558,6 +2585,48 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// ≡ › Save Selection lists the saved selections: choosing one recalls it, Update Selection
|
||||
/// makes it hold the selected objects, and Save Selection… opens the name dialog.
|
||||
#[test]
|
||||
fn the_save_selection_submenu_recalls_updates_and_saves_selections() {
|
||||
let (mut app, _, [a, b]) = two_rects();
|
||||
app.run("select.set", json!({"ids": [a.0]})).unwrap();
|
||||
app.run("select.save", json!({"name": "Left"})).unwrap();
|
||||
app.run("select.set", json!({"ids": [b.0]})).unwrap();
|
||||
app.run("select.save", json!({"name": "Right"})).unwrap();
|
||||
app.run("select.none", json!({})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
let frame = |app: &mut VectorcraftApp, events: Vec<egui::Event>| super::super::tests_appearance::frame_events(&ctx, app, events, menu);
|
||||
// Opens the submenu (when closed) and clicks the item labeled `label` in it.
|
||||
let choose = |app: &mut VectorcraftApp, label: &str| {
|
||||
let at = |texts: &[(String, egui::Rect)], label: &str| texts.iter().find(|(t, _)| t.trim() == label).map(|(_, r)| r.center());
|
||||
let mut texts = frame(app, vec![]);
|
||||
if at(&texts, label).is_none() {
|
||||
let sub = at(&texts, "Save Selection").expect("the Save Selection submenu");
|
||||
for e in [egui::Event::PointerMoved(sub), button(sub, true), button(sub, false)] {
|
||||
frame(app, vec![e]);
|
||||
}
|
||||
texts = frame(app, vec![]);
|
||||
}
|
||||
let item = at(&texts, label).unwrap_or_else(|| panic!("{label} missing from {texts:?}"));
|
||||
for e in [egui::Event::PointerMoved(item), button(item, true), button(item, false)] {
|
||||
frame(app, vec![e]);
|
||||
}
|
||||
};
|
||||
let selected = |app: &VectorcraftApp| app.session.active().unwrap().selection.objects.clone();
|
||||
choose(&mut app, "Left");
|
||||
assert_eq!(selected(&app), vec![a], "choosing a name recalls it");
|
||||
app.run("select.set", json!({"ids": [a.0, b.0]})).unwrap();
|
||||
choose(&mut app, "Update Selection");
|
||||
let saved = app.session.active().unwrap().doc.saved_selections.clone();
|
||||
assert_eq!(saved.iter().map(|s| (s.name.as_str(), s.objects.len())).collect::<Vec<_>>(), vec![("Left", 2), ("Right", 1)]);
|
||||
assert!(app.ui.dialog.is_none());
|
||||
choose(&mut app, "Save Selection…");
|
||||
let d = app.ui.dialog.clone().expect("Save Selection dialog");
|
||||
assert_eq!((d.str("__command"), d.str("name")), ("select.save".into(), "Selection 1".into()));
|
||||
}
|
||||
|
||||
/// With the dock collapsed, Layers shows in a floating flyout: its rows work there too.
|
||||
#[test]
|
||||
fn rows_work_in_the_floating_flyout() {
|
||||
|
||||
+2
-1
@@ -1042,7 +1042,8 @@ recalled. Edit Selection…
|
||||
renames and deletes: `select.editSaved {name, newName?, delete?}`, or several at once as
|
||||
`{edits: [{name, newName?, delete?}…]}` in one undo step, where every `name` is the name before the edit and the
|
||||
names must stay unique. In the desktop app the saved selections are listed at the bottom of the Select menu
|
||||
(`select.recall1` … `select.recall25`). A file's saved selections are checked when it opens: past 25, blank or
|
||||
(`select.recall1` … `select.recall25`) and in the Layers panel menu's Save Selection submenu
|
||||
(`select.recall {name}`). A file's saved selections are checked when it opens: past 25, blank or
|
||||
repeated names, and ids the document doesn't have are dropped.
|
||||
|
||||
## Ruler guides
|
||||
|
||||
@@ -484,7 +484,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
Clear deletes individual selected compound-path members without deleting their siblings, and explicit object ids work without a selection; Cut still removes the same objects it copies (#786).
|
||||
A command that leaves out an object selected inside a selected group also leaves it out when `ids` list it together with a group that holds it. Make Repeat and Live Paint no longer fail on such a list, New Symbol no longer stores the object twice, and `object.transform` no longer moves it twice.
|
||||
- **Compound paths:** Make keeps the backmost object's appearance and each contour; selecting a member in Direct Selection reads and changes the compound's fill rule in Attributes. Direction remains a per-member edit. Rendered holes, unchanged contour geometry, no-op history and Undo/Redo are covered by `engine/tests/compound_attributes.rs` (#1197). Behaviour reference: [public compound-path documentation](https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/create-compound-paths.html). Make Compound Path on a group (such as outlined type) keeps the paint of its backmost path instead of drawing nothing, and compound paths joined into one keep their holes (#1537).
|
||||
- **Saved selections:** Select ▸ Save Selection… asks for a name and keeps the selection in the document (saved with the file, undoable, up to 25); each one is listed at the bottom of the Select menu and a click selects its objects again; Edit Selection… lists them to rename or delete, applied together on OK. A file's saved selections are capped and checked against its objects when it opens. Not yet: the Layers panel's saved-selection menu.
|
||||
- **Saved selections:** Select ▸ Save Selection… asks for a name and keeps the selection in the document (saved with the file, undoable, up to 25); each one is listed at the bottom of the Select menu and a click selects its objects again; Edit Selection… lists them to rename or delete, applied together on OK. A file's saved selections are capped and checked against its objects when it opens.
|
||||
Select ▸ Update Selection replaces the objects of the saved selection last recalled with the selected objects, in one undo step that marks the document changed (`select.update`). After Edit Selection… renames that saved selection, Update Selection replaces it under the new name; after Edit Selection… deletes it, Update Selection is disabled until another saved selection is recalled or the delete is undone. Undo and Redo do not change which saved selection is recalled.
|
||||
- **Drawing modes:** in Draw Inside, a double-click with a selection tool outside the object being drawn into sets the drawing mode to Draw Normal, as Shift+D does.
|
||||
- **Drawing:** Pen, Curvature, anchor tools, Pencil, Paintbrush, Blob Brush, Smooth, Path Eraser, Join. Direction handles dragged with Direct Selection or the Anchor Point tool snap to smart guides, and Shift keeps them at 45° steps from their anchor. The Pen continues any open path from the end clicked, and while drawing, a click on an end of another open path joins the two into one (#776). The Curvature tool edits any selected path, whatever drew it, keeping its shape except where edited (#798): a click on a point makes it current and a drag moves it, re-curving only its two segments; Alt-click or double-click toggles it smooth or corner and Delete removes it; a click on a segment adds a point without changing the shape; with an end of an open path current, clicks go on from it and its other end closes it (`path.curvatureEdit`). Every point a drawing tool places snaps to Smart Guides through one snapper (#506): the Pen's and Curvature tool's anchors, the Pencil's ends, the shape tools' start and dragged corner or radius (Rectangle, Rounded Rectangle, Ellipse, Polygon, Star, Line Segment, Arc, Spiral, the grids), the Type tool's click and frame, a new artboard's corners. A filled Pencil path keeps a stroke of None (#1804, part of #1679). The [ and ] keys reach the Blob Brush, the Eraser and the Symbolism tools in the app (#1734, part of #1661).
|
||||
@@ -517,6 +517,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- **Releasing ruler guides:** View › Guides › Release Guides turns the selected ruler guides into paths with no fill or stroke. An artboard guide becomes a line across its artboard, and a canvas guide becomes a line across the artboards (`view.guides.release {index?, ids?}`).
|
||||
- **Layers panel:** every layer, sublayer, group and object is a clickable row at any depth: a click highlights it and makes its layer current (new art goes there), Shift-click a range, Ctrl/Cmd-click toggles. Sublayers are no longer treated as objects: clicks, marquees, Select All and the Select menu take the art inside them, and each sublayer selects in its own colour. Selection squares on rows with selected art (click selects a row's art, Shift adds or removes; dragging the square moves the selection to another row, Alt copies), the eye and lock on every row with drag-down-the-column (one undo step), Ctrl/Cmd-click an eye for per-layer Outline, Alt-click an eye or lock to hide or lock the other layers (again: show or unlock them all), triangles with Alt to open or close everything inside, drag and drop rows above, below or into layers and groups with a drop line (Alt copies) and a dimmed copy of the row under the pointer (dropped on the trash rows are deleted, on New Layer duplicated, as in Illustrator), inline rename, Delete key on highlighted rows. Buttons and the full panel menu: New Layer/Sublayer (Alt: options, Ctrl: on top), Duplicate, Delete, Options for Selection, clipping mask, isolation, Locate Object, Merge Selected, Flatten Artwork, Collect in New Layer, Release to Layers (Sequence and Build), Reverse Order, Template, Hide/Outline/Lock Others and Show/Preview/Unlock All, Paste Remembers Layers, Panel Options (row size, thumbnails per kind, Show Layers Only). Layer Options: name, colour (presets, each with a chip of its colour, or custom), Template, Lock, Show, Print, Preview (outline drawing and clicking) and Dim Images to %. Every operation is a command (`layer.move`, `layer.highlight`, `layer.merge`…) with validation against cycles and invalid nesting. A document opens with only its top-level layers open; the rows left open are saved in the native file (nothing is written for the default) and reopen that way. Only the rows scrolled into view are laid out, from a row list made once per change: a layer of 5,000 groups of 10 paths, all open, draws the panel in 0.4 ms a frame instead of about 29 s (#638).
|
||||
The Filter button at the right of Search All keeps the rows that are of an object kind ticked under By Object (paths, shapes, text, images, effects, groups, linked files, guides) and in a state ticked under By Layer (locked, hidden), with the rows that hold them; a section with nothing ticked takes every row. The filter combines with the search, the button is lit while a filter is on, and Clear All in its popup turns the filter off (`ui.layersFilter`). The filter lasts the session.
|
||||
The panel menu's Save Selection submenu has Save Selection…, Update Selection and Edit Selection…, enabled and run as in the Select menu, and lists the document's saved selections: choosing one selects its objects again (`select.recall`).
|
||||
`object.setProps` with `visible: false` or `locked: true` deselects every selected object that is then hidden or locked, the key object included, as the eye and lock columns do.
|
||||
Ctrl/Cmd+Alt-clicking an eye switches the other layers to Outline and the row's layer to Preview, or switches all layers back to Preview when the others are in Outline (`layer.outlineOthers`, `layer.previewAll`).
|
||||
Object › Arrange › Send to Current Layer returns the error "the target layer is locked or hidden" and leaves the selected objects where they are when the current layer is hidden or locked, or is inside a hidden or locked layer.
|
||||
|
||||
Reference in New Issue
Block a user