New Document: units belong to the chosen preset, not the whole tab

Choosing units in Preset Details restyled every preset card in the Recent
and Saved tabs, so switching between inches and millimetres rewrote all the
user's templates at once. The pane's options apply to the selected preset
(helpx, "Create and save custom document presets"), so only the chosen card
follows the chosen units now; the others keep the units their document or
preset was made in, and clicking one brings its units back. The built-in
category tabs still show the chosen units (#2078).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
FractalEncrypt
2026-11-04 21:38:01 +07:00
committed by bflatastic
co-authored by Devin
parent 69ef2a4e50
commit 985ef69cb7
6 changed files with 136 additions and 17 deletions
+1
View File
@@ -131,6 +131,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap
## Progress log
- 2026-11-04: New Document: the units chosen in Preset Details now belong to the chosen document, as the pane's options are listed for the selected preset; the chosen card shows them, every other Recent and Saved card keeps the units its document or preset was made in, and the built-in tabs still follow the chosen units, so switching between inches and millimetres no longer restyles every recent template. Scores and estimates unchanged.
- 2026-11-02: Mouse wheel over a closed font-family field in Character, Properties or Control changes selected text through the family list using saved font filters (excluding search text), one undo step per notch, stopping at the ends (#5253). Scores and estimates unchanged.
- 2026-10-31: Pattern libraries: Window › Swatch Libraries › Patterns and the Swatches panel's library menu open Geometric Patterns, a built-in library of eleven pattern swatches (dots, stripes, checks, a grid and shapes); clicking one adds its pattern and swatch to the document, once, and applies it to the active fill or stroke (`swatch.library.add`).
- 2026-11-01: Find Nearest Swatch joins the Color Effects add-on (Object › Plug-ins › Color Effects): for each colour of the selection, the closest swatches of the document, a built-in library or a library or colour book the user loaded, with their CIEDE2000 difference; Apply gives each colour its nearest swatch as one undo step, linking global and spot swatches and adding a library's to the document first, so a process colour becomes the closest spot colour (`addon.color.nearestSwatch`, `addon.color.applyNearestSwatch`, #4923).
+43 -13
View File
@@ -41,7 +41,8 @@ const PARAMS: [&str; 12] = [
"rasterEffectsPpi",
"previewMode",
];
/// The preset the fields were last filled from (its fields as `apply` set them).
/// The preset the fields were last filled from, as `apply` set them; a Recent or Saved card keeps
/// its own units here, which the units chosen in Preset Details don't change.
const APPLIED: &str = "__applied";
/// Save Preset's name field is showing.
const SAVING: &str = "__saving";
@@ -257,21 +258,50 @@ fn presets(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
ui.horizontal_wrapped(|ui| {
ui.spacing_mut().item_spacing = egui::vec2(12.0, 12.0);
let (w, h, preset, units) = (d.f64("width", 0.0), d.f64("height", 0.0), d.str("preset"), unit(d));
// The fields were last filled from this preset's card; Recent keeps one entry per size
// and units, so the units tell two entries of one size apart.
let chosen = d
.fields
.get(APPLIED)
.filter(|a| a.get("preset").and_then(Value::as_str) == Some(preset.as_str()))
.and_then(Value::as_object)
.cloned();
for s in &list {
// The built-in presets' cards show the chosen orientation (square sizes have none)
// and the units chosen in Preset Details; choosing one keeps both. Recent and Saved
// show each entry as it was made, so a portrait and a landscape A3 are two cards
// (#2860).
let mut s = DocSettings { units, ..s.clone() };
if cat > 1 && ((w > h && s.height > s.width) || (h > w && s.width > s.height)) {
std::mem::swap(&mut s.width, &mut s.height);
// The built-in presets' cards show the chosen orientation (square sizes have none);
// Recent and Saved show each entry as it was made, so a portrait and a landscape A3
// are two cards (#2860).
let mut card = s.clone();
if cat > 1 && ((w > h && card.height > card.width) || (h > w && card.width > card.height)) {
std::mem::swap(&mut card.width, &mut card.height);
}
let selected = d.fields.contains_key(APPLIED) && s.name == preset && s.width == w && s.height == h;
let card = preset_card(ui, &s, selected);
if card.clicked() || card.double_clicked() {
apply(d, &s);
// The built-in presets all share the units chosen in Preset Details.
if cat > 1 {
card.units = units;
}
create |= card.double_clicked();
let selected = chosen.as_ref().is_some_and(|a| {
a.get("preset").and_then(Value::as_str) == Some(card.name.as_str())
&& a.get("width").and_then(Value::as_f64) == Some(card.width)
&& a.get("height").and_then(Value::as_f64) == Some(card.height)
&& (cat > 1 || a.get("units").and_then(Value::as_str) == Some(s.units.label()))
});
// The chosen card shows the units chosen in Preset Details, which the new document
// will have (#2078); the others keep their own, so choosing one document's units
// doesn't restyle the whole tab.
if selected {
card.units = units;
}
let resp = preset_card(ui, &card, selected);
if resp.clicked() || resp.double_clicked() {
apply(d, &card);
// The card is still the entry it was, whatever units it shows: its own units say
// which entry it is, so a chosen card stays chosen after a units change.
if cat < 2
&& let Some(Value::Object(a)) = d.fields.get_mut(APPLIED)
{
a.insert("units".into(), json!(s.units.label()));
}
}
create |= resp.double_clicked();
}
});
});
+85 -1
View File
@@ -5,7 +5,7 @@
use serde_json::json;
use vectorcraft_doc::Unit;
use super::tests_appearance::{app_with_rect, frame_events, run};
use super::tests_appearance::{app_with_rect, click, frame_events, run, text_rect};
use super::*;
/// Every text drawn in the second headless frame of `f` (windows size themselves in the first).
@@ -171,3 +171,87 @@ fn new_document_cards_follow_the_chosen_units() {
assert!(shows(&t, "288 × 432 px") && !shows(&t, "101.6 × 152.4 mm"), "Postcard in px: {t:?}");
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["preset"], json!("Postcard"));
}
/// Choosing units in Preset Details changes the chosen document, not every recent one: each card
/// keeps the units its document was made in, so switching between inches and millimetres leaves the
/// other templates as they were; choosing another card brings its units back.
#[test]
fn recent_cards_keep_their_own_units() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
// 8.5 × 11 in, then 210 × 297 mm, which Recent lists first and the dialog opens on.
run(&mut app, "file.new", json!({"width": "8.5 in", "height": "11 in", "units": "Inches"}));
run(&mut app, "file.new", json!({"width": "210 mm", "height": "297 mm", "units": "Millimeters"}));
app.run("file.newDialog", json!({})).unwrap();
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["units"], json!("Millimeters"));
app.ui.dialog.as_mut().unwrap().fields.insert("units".into(), json!("Points"));
let t = dialog_texts(&mut app);
assert!(shows(&t, "595.276 × 841.89 pt"), "the chosen entry in points: {t:?}");
assert!(shows(&t, "8.5 × 11 in"), "the other entry keeps its inches: {t:?}");
assert!(!shows(&t, "612 × 792 pt"), "the inches entry isn't turned into points: {t:?}");
// Clicking the inches card makes it the chosen one, in its own units.
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut draw = |app: &mut VectorcraftApp, ui: &mut Ui| crate::dialogs::show(app, ui.ctx());
frame_events(&ctx, &mut app, vec![], &mut draw); // the window sizes itself here
let inches = text_rect(&frame_events(&ctx, &mut app, vec![], &mut draw), "8.5 × 11 in").center();
click(&ctx, &mut app, inches, &mut draw);
let d = app.ui.dialog.as_ref().unwrap();
assert_eq!(d.fields["units"], json!("Inches"));
assert_eq!((d.fields["width"].clone(), d.fields["height"].clone()), (json!(612.0), json!(792.0)));
}
/// A saved preset keeps the units it was saved in too, whichever units the dialog is showing.
#[test]
fn saved_cards_keep_their_own_units() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
run(&mut app, "file.newPresets.save", json!({"name": "A4 mm", "width": "210 mm", "height": "297 mm", "units": "Millimeters"}));
app.run("file.newDialog", json!({})).unwrap();
let d = app.ui.dialog.as_mut().unwrap();
d.fields.insert("category".into(), json!("Saved"));
d.fields.insert("units".into(), json!("Points"));
let t = dialog_texts(&mut app);
assert!(shows(&t, "210 × 297 mm") && !shows(&t, "595.276 × 841.89 pt"), "the saved preset keeps millimetres: {t:?}");
}
/// Clicking the card that is already chosen leaves it chosen, in the units the dialog is showing:
/// the entry's own units still say which card it is.
#[test]
fn clicking_the_chosen_recent_card_keeps_it_chosen() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
run(&mut app, "file.new", json!({"width": "8.5 in", "height": "11 in", "units": "Inches"}));
app.run("file.newDialog", json!({})).unwrap();
app.ui.dialog.as_mut().unwrap().fields.insert("units".into(), json!("Points"));
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut draw = |app: &mut VectorcraftApp, ui: &mut Ui| crate::dialogs::show(app, ui.ctx());
frame_events(&ctx, &mut app, vec![], &mut draw); // the window sizes itself here
let card = text_rect(&frame_events(&ctx, &mut app, vec![], &mut draw), "612 × 792 pt").center();
click(&ctx, &mut app, card, &mut draw);
let t = dialog_texts(&mut app);
assert!(shows(&t, "612 × 792 pt") && !shows(&t, "8.5 × 11 in"), "the card stays chosen in points: {t:?}");
}
/// Recent keeps one entry per size and units, so two entries can be one size in two units: the
/// chosen card is the entry the fields were filled from, not every card of that size.
#[test]
fn recent_entries_of_one_size_in_two_units_stay_apart() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
// Both are 612 × 792 pt; Recent lists the points one first, which the dialog opens on.
run(&mut app, "file.new", json!({"width": "8.5 in", "height": "11 in", "units": "Inches"}));
run(&mut app, "file.new", json!({"width": "612 pt", "height": "792 pt", "units": "Points"}));
app.run("file.newDialog", json!({})).unwrap();
app.ui.dialog.as_mut().unwrap().fields.insert("units".into(), json!("Inches"));
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut draw = |app: &mut VectorcraftApp, ui: &mut Ui| crate::dialogs::show(app, ui.ctx());
frame_events(&ctx, &mut app, vec![], &mut draw); // the window sizes itself here
let texts = frame_events(&ctx, &mut app, vec![], &mut draw);
let mut cards: Vec<egui::Rect> = texts.iter().filter(|(t, _)| t == "8.5 × 11 in").map(|(_, r)| *r).collect();
cards.sort_by(|a, b| a.center().x.total_cmp(&b.center().x));
assert_eq!(cards.len(), 2, "both cards read in inches: {texts:?}");
// The right-hand card is the inches entry: choosing it leaves the points entry in points.
click(&ctx, &mut app, cards[1].center(), &mut draw);
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["units"], json!("Inches"));
let t = dialog_texts(&mut app);
assert!(shows(&t, "612 × 792 pt"), "the points entry keeps its points: {t:?}");
}
+3 -1
View File
@@ -1,6 +1,6 @@
# Roadmap: milestones and what's next
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (OpenType panel offers font-defined Stylistic Sets; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-04 · **Change:** minor (New Document's units belong to the chosen preset; Recent and Saved cards keep their own units; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
## Feature submission evidence
@@ -28,6 +28,7 @@ M7 type: Touch Type moves a right-to-left character along its line and up from i
| Date | Change | Summary |
|---|---|---|
| 2026-11-04 | minor | New Document: the units chosen in Preset Details belong to the chosen preset; Recent and Saved cards keep the units their document or preset was made in, and the built-in tabs still follow the chosen units (#2078). Scores and estimates unchanged. |
| 2026-11-03 | minor | M1: Enclosing Lasso provides whole-object enclosure selection as an add-on with no default shortcut or Basic toolbar slot; estimates unchanged. |
| 2026-11-03 | minor | New gradients use CMYK default stops in CMYK documents, including Gradient tool creation. Scores and estimates unchanged. |
| 2026-10-30 | minor | M4: G9: `.ai`/EPS editing data: a blend takes the transparency (opacity, blend mode, knockout) in its state block after its record, so its steps knock each other out as the page draws them; a user's file comes in 0.6% from its page instead of 1.3%. Scores and estimates unchanged. |
@@ -158,6 +159,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
| Date | Change | Summary |
|---|---|---|
| 2026-11-04 | minor | New Document: the units chosen in Preset Details belong to the chosen preset; Recent and Saved cards keep their own units; scores and estimates unchanged. |
| 2026-11-03 | minor | M7: the OpenType panel offers the selected font's available Stylistic Sets with custom or numbered names; estimates unchanged. |
| 2026-11-03 | minor | M1: Enclosing Lasso adds freehand whole-object selection under Object › Plug-ins; estimates unchanged. |
| 2026-11-02 | minor | Font-family fields in Character, Properties and Control step selected text with the wheel (#5253), with one undo step per notch; scores and estimates unchanged. |
+2 -1
View File
@@ -1,6 +1,6 @@
# Parity with Adobe Illustrator
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-04 · **Change:** minor (Document Tools add-on: Batch Trace Folder, #4921; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-04 · **Change:** minor (New Document's units belong to the chosen preset; Recent and Saved cards keep their own units; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
## Feature submission evidence
@@ -23,6 +23,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard
| Date | Change | Summary |
|---|---|---|
| 2026-11-04 | minor | New Document: the units chosen in Preset Details belong to the chosen preset, as the public page lists the pane's options for the selected preset; the chosen card shows them, every other Recent and Saved card keeps the units its document or preset was made in, and the built-in tabs still follow the chosen units (#2078). Scores and estimates unchanged. |
| 2026-11-04 | minor | Document Tools add-on (a VectorCraft addition): Batch Trace Folder under Object › Plug-ins (`addon.document.batchTraceFolder`, #4921); evidence added; scores unchanged. |
| 2026-11-04 | minor | Object › Expand turns selected symbol instances into their art as Break Link places it, one level deep, with the Expand dialog's Fill and Stroke reaching that art. Scores and estimates unchanged. |
| 2026-11-03 | minor | The OpenType panel offers the selected font's stylistic sets by custom or numbered name, and allows several to be active. Scores and estimates unchanged. |
+2 -1
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-04 · **Change:** minor (Document Tools add-on: Batch Trace Folder under Object › Plug-ins, #4921; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-04 · **Change:** minor (New Document's units belong to the chosen preset; Recent and Saved cards keep their own units; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
## Feature submission evidence
@@ -423,6 +423,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
| Date | Change | Summary |
|---|---|---|
| 2026-11-04 | minor | New Document: the units chosen in Preset Details belong to the chosen document, as the pane's options are listed "for the selected preset" ([public source](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/start-a-new-file/create-and-save-custom-document-presets.html)); the chosen card shows them, every other Recent and Saved card keeps the units its document or preset was made in, and the built-in tabs still follow the chosen units (#2078); scores and estimates unchanged |
| 2026-11-01 | minor | Object › Image Trace › Batch Trace Folder asks for a source and a destination folder and traces each image into it (#4921); scores and estimates unchanged |
| 2026-11-01 | minor | Edit › Copy and Cut take the selected ruler guides, with the objects or alone; pasting puts them on the layer new art goes to, moved as the objects are (alone: where they were), Paste in Place onto the active artboard and Paste on All Artboards onto each (#3226); scores and estimates unchanged |
| 2026-11-04 | minor | Area type: Align Top / Center / Bottom / Justify Vertically buttons in the Properties panel (Paragraph section) and the Control panel set its vertical alignment in the frame, as Area Type Options does; turned off for frames with rows or columns and threaded frames (#3759); scores and estimates unchanged |