File formats: File › Open has filters for graphic style libraries and perspective grid presets

File › Open already opened `.vcstyles` graphic style libraries in their
library panel and imported `.vcperspective` perspective grid presets, but
its dialog had no filter for either. It now lists "Graphic Style
Libraries" after "Swatch libraries" and "Perspective grid presets" after
"Print presets". "All readable files" still lists documents only.

Other Library… and Locate Library… in the swatch and graphic style
library panels list their library filter once; the swatch dialog listed
"Swatch libraries" twice.
This commit is contained in:
dotCooCoo
2026-11-12 15:55:58 -08:00
parent b4505a76de
commit 2a5d255217
3 changed files with 30 additions and 10 deletions
+11 -8
View File
@@ -782,9 +782,9 @@ pub fn open_extensions() -> &'static [&'static str] {
/// File › Open's filters: "All readable files" first, then one per readable format, then one per
/// importer plug-in (under its name, with the extensions [`open_extensions`] takes from it), then
/// swatch libraries (which open in the library panel), Libraries panel libraries, flattener, PDF,
/// print and export presets (imported), scripts (checked without running them) and plug-ins
/// (installed).
/// swatch and graphic style libraries (which open in their library panels), Libraries panel
/// libraries, flattener, PDF, print, perspective grid and export presets and composite fonts
/// (imported), shortcut sets, scripts (checked without running them) and plug-ins (installed).
pub fn open_filters() -> impl Iterator<Item = (&'static str, &'static [&'static str])> {
let (all, importers) = importer_open_part();
std::iter::once(("All readable files", all)).chain(format_filters()).chain(importers).chain(other_open_filters())
@@ -797,14 +797,17 @@ pub fn builtin_open_filters() -> impl Iterator<Item = (&'static str, &'static [&
std::iter::once(("All readable files", OPEN_EXTS)).chain(format_filters()).chain(other_open_filters())
}
/// The open-dialog filters after the document formats: swatch libraries, Libraries panel
/// libraries, presets, composite fonts, shortcut sets, scripts and plug-ins.
/// The open-dialog filters after the document formats: swatch and graphic style libraries,
/// Libraries panel libraries, presets (perspective grid presets among them), composite fonts,
/// shortcut sets, scripts and plug-ins.
fn other_open_filters() -> impl Iterator<Item = (&'static str, &'static [&'static str])> {
std::iter::once(("Swatch libraries", crate::cmd::swatchlib::LIBRARY_EXTS))
.chain(std::iter::once(("Graphic Style Libraries", std::slice::from_ref(&vectorcraft_doc::style_libs::STYLES_EXT))))
.chain(std::iter::once(("Libraries", crate::cmd::library::LIBRARY_EXTS)))
.chain(std::iter::once(("Flattener presets", crate::cmd::flatten::PRESET_EXTS)))
.chain(std::iter::once(("PDF presets", crate::cmd::pdfcmds::PRESET_EXTS)))
.chain(std::iter::once(("Print presets", crate::cmd::printpresets::PRESET_EXTS)))
.chain(std::iter::once(("Perspective grid presets", crate::cmd::perspgrid::PRESET_EXTS)))
.chain(std::iter::once(("Export presets", screens::PRESET_FILE_EXTS)))
.chain(std::iter::once(("Composite fonts", crate::cmd::compositefonts::EXTS)))
.chain(std::iter::once(("Shortcut sets", crate::cmd::shortcutset::EXTS)))
@@ -815,13 +818,13 @@ fn other_open_filters() -> impl Iterator<Item = (&'static str, &'static [&'stati
/// Open-dialog filters for Swatch Libraries and Graphic Style Libraries Other Library… and Locate
/// Library…. The library formats come first so Linux file portals (which grey out files that don't
/// match the default filter) still list `.ase` and the other library extensions; then the same
/// filters as [`builtin_open_filters`] so a document can be picked for its swatches or graphic
/// styles.
/// filters as [`builtin_open_filters`], without a second copy of the library filter, so a document
/// can be picked for its swatches or graphic styles.
pub fn library_open_filters(
library_label: &'static str,
library_exts: &'static [&'static str],
) -> impl Iterator<Item = (&'static str, &'static [&'static str])> {
std::iter::once((library_label, library_exts)).chain(builtin_open_filters())
std::iter::once((library_label, library_exts)).chain(builtin_open_filters().filter(move |f| *f != (library_label, library_exts)))
}
/// File → Place dialog filters: "All placeable files", then one per placeable format, then text,
+18 -2
View File
@@ -390,9 +390,20 @@ fn open_exts_cover_every_readable_format() {
assert_eq!(filters[0], ("All readable files", OPEN_EXTS));
assert_eq!(
filters.len(),
11 + FORMATS.iter().filter(|f| f.read).count(),
"and swatch libraries, libraries, flattener, PDF, print and export presets, composite fonts, shortcut sets, scripts, plug-ins"
13 + FORMATS.iter().filter(|f| f.read).count(),
"and swatch and graphic style libraries, libraries, flattener, PDF, print, perspective grid and export presets, composite \
fonts, shortcut sets, scripts, plug-ins"
);
let at = |label: &str| filters.iter().position(|f| f.0 == label).unwrap_or_else(|| panic!("File › Open has no {label} filter"));
let styles: &[&str] = &[vectorcraft_doc::style_libs::STYLES_EXT];
assert_eq!(filters[at("Graphic Style Libraries")].1, styles, "File › Open opens graphic style libraries");
assert_eq!(at("Graphic Style Libraries"), at("Swatch libraries") + 1, "next to the swatch libraries");
assert_eq!(filters[at("Perspective grid presets")].1, crate::cmd::perspgrid::PRESET_EXTS, "File › Open imports perspective grid presets");
assert_eq!(at("Perspective grid presets"), at("Print presets") + 1, "among the presets");
assert_eq!(at("Export presets"), at("Perspective grid presets") + 1, "among the presets");
for e in [vectorcraft_doc::style_libs::STYLES_EXT].iter().chain(crate::cmd::perspgrid::PRESET_EXTS) {
assert!(!filters[0].1.contains(e), "All readable files lists documents only, not .{e}");
}
assert!(filters.contains(&("Export presets", SCREEN_EXPORT_PRESET_EXTS)), "File › Open imports export presets");
assert!(filters.contains(&("Scripts", crate::cmd::scripts::EXTS)), "File › Open checks scripts");
assert_eq!(filters.last(), Some(&("Plug-ins", crate::cmd::plugin::EXTS)), "File › Open installs plug-ins");
@@ -404,6 +415,11 @@ fn library_open_filters_list_library_formats_first() {
assert_eq!(filters[0], ("Swatch libraries", crate::cmd::swatchlib::LIBRARY_EXTS));
assert!(filters[0].1.contains(&"ase"));
assert_eq!(filters[1], ("All readable files", OPEN_EXTS));
let styles: &'static [&'static str] = std::slice::from_ref(&vectorcraft_doc::style_libs::STYLES_EXT);
for (label, exts) in [("Swatch libraries", crate::cmd::swatchlib::LIBRARY_EXTS), ("Graphic Style Libraries", styles)] {
let filters: Vec<_> = library_open_filters(label, exts).collect();
assert_eq!(filters.iter().filter(|f| f.0 == label).count(), 1, "{label} is listed once");
}
}
/// The extensions usage texts list for export: one per writable format, each picking that format
+1
View File
@@ -862,6 +862,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- The macOS menu bar is built from the in-window menus (one model, `native_menu`) and laid out as a Mac app's: the VectorCraft menu holds About, Settings ▸ (each Preferences page, General… ⌘K), Language ▸, Appearance ▸ (UI brightness), Services, Hide VectorCraft ⌃⌘H (⌘H stays Hide Edges), Hide Others, Show All and Quit; Window gains Minimize ⌃⌘M, Zoom and Bring All to Front, and Help the system's search field. Its key equivalents go back to egui as key presses, so user shortcuts, focused text fields and the Type tool's input method behave as in the window, and nothing fires twice; enabled, check marks and labels update in place. `--in-window-menus` or `VECTORCRAFT_IN_WINDOW_MENUS=1` keeps the menus in the window; `ui.inspect` reports `nativeMenuBar`.
- macOS: Finder opens every format File › Open reads with VectorCraft (double-click, Open With, a drop on the Dock icon, `open -a`), at launch or while it runs; the release and `cargo xtask bundle` apps share one `Info.plist` that declares those document types, owning only VectorCraft's own formats. The DMG opens on a designed Finder window, VectorCraft.app beside the `Applications` link over a background with the app icon (#493).
- Swatch and graphic style libraries, Libraries panel files, presets and plug-ins open wherever they are dropped on the window, as File › Open opens them.
- File › Open has a filter for each kind of file it opens other than a document, Graphic Style Libraries (`.vcstyles`, after Swatch libraries) and Perspective grid presets (`.vcperspective`, after Print presets) among them, and Other Library… lists its library filter once. Graph designs are stored in the document, with no file type or filter of their own. Tests: `open_exts_cover_every_readable_format`, `library_open_filters_list_library_formats_first`.
- **Symbol libraries** ([public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-or-import-symbol-libraries.html)): Window › Symbol Libraries and the Symbols panel's Symbol Libraries Menu open each library in a library panel of its own, as swatch and graphic style libraries do. The built-in libraries, Basic Shapes, UI Frames (Panel, Button and Bar with default 9-slice guides), Map Markers (Pin, Flag, Crosshair and Checkpoint, no default 9-slice), Inventory Icons (Sword, Shield, Potion, Key and Gem, no default 9-slice) and Skill Icons (Attack, Defend, Heal, Buff and Ult, no default 9-slice), are original art generated in code (`crates/engine/src/cmd/symbollib.rs`). A click on a symbol adds it to the document's Symbols as one undo step, with its Symbol Options (size, registration, export type, 9-slice guides) and every resource it uses (the symbols it nests, patterns, swatches, brushes, graphic styles, images), and makes it the current symbol; a symbol the document already has is not added again and another symbol of the same name keeps its name, the library's comes in numbered. Thumbnails, in the library panels and the Symbols panel, draw a symbol with the symbols it nests and the patterns and images it uses. Dragging a library symbol onto the canvas also places an instance there, in the same step ([placing from a library](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-and-place-symbols.html)). Save Symbols (the library menu) and Save Symbol Library… (the Symbols panel menu) write the document's symbols as a `.vectorcraft` document holding only them, into the user library folder (listed under User Defined) or a file; Other Library… reads the symbols of any document VectorCraft opens; a file it can't read is an error and opens no panel. Tests: `crates/engine/src/tests/symbollib.rs`, `crates/ui-egui/src/panels/tests_symbol_libraries.rs`, `panels::symbols::tests::thumbnails_show_the_symbols_a_symbol_nests`, `canvas::tests::a_library_symbol_dropped_on_the_canvas_is_added_and_placed_there_in_one_step`.
- **9-slice scaling** ([public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/maintain-proportions-while-scaling-symbols.html)): a scaled instance of a symbol with Enable Guides for 9-Slice Scaling on stays an instance and keeps the parts of its art outside the guides at their size. New guides sit a quarter of the symbol's width and height in from each edge ([reference](https://help.adobe.com/archive/en/illustrator/cs6/illustrator_reference.pdf), Use 9-slice scaling section); saved guides keep their values. The rest is VectorCraft's own rule: the corners keep both sizes, the top and bottom edges stretch in width, the left and right edges in height and the center in both; an instance smaller than its two outer cells on an axis shrinks them in proportion and leaves the center empty; the cells are sized along the symbol's axes before the instance's rotation, shear or reflection. A path with solid paint that crosses a guide is cut there and stays one path, its stroke at its weight with Scale Strokes & Effects on or off; type, images, live objects and gradient or pattern paint that cross a guide are drawn in each cell they reach, clipped to it, except those that draw symbol art (a nested instance, type with an inline graphic, a placed document, a brushed stroke, or a pattern whose tile draws one of these), which are placed by the cell their center is in; an object in one cell is placed by that cell and stays as it is. The canvas, bounds, the Eyedropper, Break Link to Symbol and the PDF, EPS, DXF, EMF/WMF and SVG exports place the art the same way; SVG writes such an instance as its placed art instead of a `<use>`, and DXF and WMF, which do not clip, cut every path that crosses a guide and place other such objects by the cell their center is in. Tests: `symbol_nine_slice::tests` in `crates/doc`, `crates/engine/src/tests/symbol_nine_slice.rs`, `a_scaled_nine_slice_instance_keeps_its_corners` (PDF), `a_scaled_nine_slice_instance_is_written_as_the_canvas_draws_it` (SVG), `a_scaled_nine_slice_instance_is_sampled_as_it_is_drawn` (Eyedropper).
- **Symbol editing mode** ([Edit symbols](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/edit-symbols.html), [Symbols panel options](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/symbols-panel-options.html)): a double-click on a symbol instance, the Control bar's Edit Symbol button, a double-click on a symbol in the Symbols panel and the panel menu's Edit Symbol (`symbol.edit`) open the symbol's art on a temporary isolated layer, where the instance shows it (from the panel: centered on the active artboard, or in the view in a document without artboards), with the rest of the document dimmed. Each edit there changes every instance at once; the instances keep their own transforms and stay linked (Break Link to Symbol still expands one), and a dynamic symbol's instances keep their appearance changes to the objects the art still has. Escape, the isolation bar's back button, the Control bar's Exit Symbol Editing Mode and a double-click outside the art leave the mode (`symbol.exitEditing`); leaving without an edit adds no undo step. Saving leaves the editing layer out. Tests: `crates/engine/src/tests/symboledit.rs`, `select::tests::a_double_click_on_a_symbol_instance_edits_its_symbol`, `panels::symbols::tests::edit_symbol_from_the_panel_centers_the_art_on_the_active_artboard_or_the_view`.