Merge pull request #5614 from FractalEncrypt/fix/new-doc-units-per-template

New Document: units belong to the chosen preset, not the whole tab
This commit is contained in:
BFlat
2026-11-04 21:56:32 +07:00
committed by GitHub
7 changed files with 139 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).
+3
View File
@@ -201,3 +201,6 @@ sync_github_name = false
[people.FrancoJacquet]
real_name = "Franco Jacquet"
[people.FractalEncrypt]
sync_github_name = true
+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 |