diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index ba85dc4a2..acbad1750 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -468,9 +468,10 @@ pub fn cancel(app: &mut VectorcraftApp) { missing_fonts::requeue_replaced(app); if let Some(d) = app.ui.dialog.take() { // Toolbar-only Flare options start no preview and must not cancel a gesture - // that was already active before opening them. + // that was already active before opening them. The generic parameter dialog has a spec + // without a preview, yet previews some commands (Grid Repeat Options) when asked to. let owns_preview = d.kind != flare_options::KIND || d.fields.contains_key(form::PREVIEWED); - if spec(&d.kind).preview && owns_preview { + if (spec(&d.kind).preview && owns_preview) || d.fields.contains_key(form::PREVIEWED) { let _ = app.session.cancel_interaction(); } app.ui.dialog = parent(&d); diff --git a/crates/ui-egui/src/dialogs/command.rs b/crates/ui-egui/src/dialogs/command.rs index 3ebefdc21..89ef90c88 100644 --- a/crates/ui-egui/src/dialogs/command.rs +++ b/crates/ui-egui/src/dialogs/command.rs @@ -16,13 +16,33 @@ pub(super) const SPEC: DialogSpec = DialogSpec { let (lengths, order) = (lengths(&command), order(&command)); let rank = |k: &str| order.iter().position(|o| *o == k).unwrap_or(order.len()); form::param_fields(ui, d, &|k| lengths.contains(&k), &|k| choices(&command, k), &rank, app.session.general_unit()); + if previews(d) { + let (cmd, params) = call(d); + form::preview(app, ui, d, &d.str("__label"), &cmd, params); + } false }, confirm, - passes: takes, + passes: |d, key| takes(d, key) || (key == "preview" && previews(d)), ..DialogSpec::FORM }; +/// Does this dialog show a Preview checkbox? Only Grid Repeat Options does: the public page for +/// the grid options lists one, and the radial and mirror pages do not. (Cancel rolls a preview +/// back whichever dialog started it.) +fn previews(d: &Dialog) -> bool { + d.str("__command") == "object.repeat.options" && d.fields.contains_key("gridType") +} + +/// The command the dialog runs and its parameters: the fields shown and the fixed ones. +fn call(d: &Dialog) -> (String, Value) { + let mut params = form::params(d); + if let (Some(o), Some(Value::Object(fixed))) = (params.as_object_mut(), d.fields.get("__params")) { + o.extend(fixed.clone()); + } + (d.str("__command"), params) +} + /// Does the dialog's command take parameter `key` (its params description names it)? fn takes(d: &Dialog, key: &str) -> bool { let id = d.str("__command"); @@ -100,12 +120,12 @@ const PIE_SORT: form::Choices = &[("All", "all"), ("First", "first"), ("None", " /// Graph Type › Tick Marks › Length. const GRAPH_TICK_LENGTH: form::Choices = &[("None", "none"), ("Short", "short"), ("Full Width", "full")]; -/// Closes before running, so a dialog the command opens stays open. +/// Closes before running, so a dialog the command opens stays open. A previewed one keeps its +/// preview as the one undo step. fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { - let cmd = d.str("__command"); - let mut params = form::params(d); - if let (Some(o), Some(Value::Object(fixed))) = (params.as_object_mut(), d.fields.get("__params")) { - o.extend(fixed.clone()); + let (cmd, params) = call(d); + if previews(d) { + return form::commit_preview(app, &cmd, params); } app.ui.dialog = None; // A tool's click-to-size shape (Flare) goes on the active perspective plane while the grid shows. @@ -192,4 +212,82 @@ mod tests { assert_eq!(app.session.execute("graph.setType", &json!({})).unwrap()["type"], "stackedBar"); assert_eq!(app.session.execute("graph.setData", &json!({})).unwrap(), data); } + + /// A document with a rectangle made a 2 x 2 Grid Repeat and selected, its Repeat Options + /// dialog open. + fn grid_options_app() -> VectorcraftApp { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 400, "height": 400})).unwrap(); + let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 20, "height": 20})).unwrap()["id"].as_u64().unwrap(); + app.run("object.repeat.grid", json!({"ids": [id], "rows": 2, "cols": 2})).unwrap(); + crate::menus::invoke(&mut app, "object.repeat.options", json!({})); + assert_eq!(app.ui.dialog.as_ref().unwrap().kind, "command"); + app + } + + /// One headless frame of the open dialog. + fn frame(app: &mut VectorcraftApp) { + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let mut out = ctx.run_ui(egui::RawInput::default(), |ui| crate::dialogs::show(app, ui.ctx())); + out.textures_delta.clear(); + } + + fn cols(app: &VectorcraftApp) -> u64 { + crate::panels::pattern_options::repeat_fields(app).unwrap()["cols"].as_u64().unwrap() + } + + /// #2651: the grid options' public page lists a Preview checkbox: with it on, the repeat on + /// the canvas follows the fields while the dialog is open; Cancel puts it back without a + /// step of history; OK keeps what was previewed as one step. + #[test] + fn grid_repeat_options_preview_on_the_canvas() { + let mut app = grid_options_app(); + let steps = app.session.doc().unwrap().history.undo.len(); + // Off: the fields change nothing until OK. + app.ui.dialog.as_mut().unwrap().fields.insert("cols".into(), json!(3)); + frame(&mut app); + assert_eq!(cols(&app), 2, "no preview without the checkbox"); + // On, the repeat follows the fields. + crate::dialogs::set_field(app.ui.dialog.as_mut().unwrap(), "preview".into(), json!(true)); + frame(&mut app); + assert_eq!(cols(&app), 3, "the canvas shows 3 columns while the dialog is open"); + app.ui.dialog.as_mut().unwrap().fields.insert("cols".into(), json!(4)); + frame(&mut app); + assert_eq!(cols(&app), 4, "and follows the next edit"); + // Cancel puts the original back and leaves no history. + crate::dialogs::cancel(&mut app); + assert_eq!(cols(&app), 2, "Cancel restores the original repeat"); + assert_eq!(app.session.doc().unwrap().history.undo.len(), steps, "and adds no undo step"); + assert!(!app.session.in_interaction()); + // OK keeps the previewed values as one undo step. + crate::menus::invoke(&mut app, "object.repeat.options", json!({})); + let d = app.ui.dialog.as_mut().unwrap(); + d.fields.insert("cols".into(), json!(5)); + crate::dialogs::set_field(d, "preview".into(), json!(true)); + frame(&mut app); + crate::dialogs::confirm(&mut app).unwrap(); + assert!(app.ui.dialog.is_none()); + assert_eq!(cols(&app), 5); + assert_eq!(app.session.doc().unwrap().history.undo.len(), steps + 1, "one undo step"); + app.run("edit.undo", json!({})).unwrap(); + assert_eq!(cols(&app), 2, "one Undo takes the whole change back"); + } + + /// Radial and Mirror Repeat Options have no Preview (their public pages list none): their + /// fields apply on OK only, and the dialog takes no `preview`. + #[test] + fn radial_repeat_options_have_no_preview() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 400, "height": 400})).unwrap(); + let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 20, "height": 20})).unwrap()["id"].as_u64().unwrap(); + app.run("object.repeat.radial", json!({"ids": [id], "instances": 4})).unwrap(); + crate::menus::invoke(&mut app, "object.repeat.options", json!({})); + let d = app.ui.dialog.as_ref().unwrap(); + assert!(!crate::dialogs::accepts_field(d, "preview")); + app.ui.dialog.as_mut().unwrap().fields.insert("instances".into(), json!(7)); + frame(&mut app); + assert_eq!(crate::panels::pattern_options::repeat_fields(&app).unwrap()["instances"], 4); + assert!(!app.session.in_interaction()); + } } diff --git a/docs/gaps.md b/docs/gaps.md index 646ffa864..608779f7a 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -1,6 +1,6 @@ # Gaps -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G8: a gradient mesh from a file is budgeted at about a million patches and quads, #3592; estimate unchanged; previously: G13: gradient presets done; G1: Average & Join with a corner or smooth choice, Cmd+Alt+Shift+J, done; estimate unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G1: Grid Repeat Options has a Preview checkbox, #2651; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence @@ -94,6 +94,7 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall - **Done:** the Properties panel has the additional contextual controls of the public Properties panel overview: Blend Options in Quick Actions for one blend with the Blend tool, Merge Live Paint for a Live Paint group selected with more paths to merge (Live Paint Make and Merge refuse type, images and other objects that aren't paths, which used to vanish among the group's sources), the Width Profile list in Appearance with the Width tool or a variable-width stroke, an applied effect's Delete icon beside fx (with several effects it opens the Appearance panel), and Corner and Corner Radius for corners selected with Direct Selection (public doc cited in target-app-parity.md). Broader G1 estimates unchanged. - **Done (#2691):** a selected Grid Repeat ([public page](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html)) shows a handle below the grid, one right of it and one past its bottom-right corner: dragging them adds or removes rows, columns or both (1 to 500). A slider between the first two columns and one between the first two rows set the horizontal and vertical spacing, staying halfway across the gap under the pointer. Each drag is one undo step through `object.repeat.options` and Escape cancels it; the controls hide with the edges or the bounding box, and bounding-box handles keep priority where they meet. Radial Repeat handles remain. Estimates unchanged. +- **Done (#2651):** Object › Repeat › Options… for a Grid Repeat has a Preview checkbox ([public page](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html)): while it is on the repeat on the canvas follows the fields, Cancel puts the original back without a history step and OK keeps the previewed values as one undo step. Radial and Mirror Repeat Options have none, as their public pages list none. Estimate unchanged. - **Done (#2877):** a selected Mirror Repeat shows its axis: drag the line or the center handle to move it, an end handle to rotate it (Shift: 45°); each drag is one undo step and Escape cancels it. Bounding-box handles take priority over the axis line; the center and end handles keep their actions. Broader G1 estimates unchanged. - **Existing-guide Escape cancellation:** Selection and Direct/Group Selection clear pending presses and cancel begun guide previews. App-input regressions cover preselected-guide document and interaction-start selection restoration, Undo/Redo and saved state, and later on-canvas motion/release. All eight app-input checks and the Direct Selection Escape control passed in current full CI; [Validation evidence](target-app-parity.md#evidence-what-ships-today-by-area). The broader G1 scope and estimate remain unchanged. @@ -677,6 +678,7 @@ trap, and the defaults (0.25 pt, 100%, 40%) are VectorCraft's own, the pages giv | 2026-10-26 | minor | G9: EPS save-back: an opened EPS saves back to its own path as EPS, keeping the chosen artboard's page frame and transparent margins and the complete editable VectorCraft document; Save's TIFF preview is capped at 1024 px (Export keeps 2048); partly read, preview-only or damaged-editing-data sources still need confirmation before they are overwritten. This narrows G9's save-back workflow only; its import-fidelity items (layer and group structure, guides, open speed, live objects) and estimate are unchanged, and writing Illustrator's own editing data stays out of scope (#868) | | 2026-10-26 | minor | G7: View › New View… / Edit Views… ([public source](https://helpx.adobe.com/illustrator/using/viewing-artwork.html)): the View menu lists every one of a document's 25 saved views (it listed 10), Edit Views… lists them to rename or delete in one undo step (`view.saved.edit {edits}`), and a file's views are checked when it opens; New View/Edit Views leave G7's missing list; estimate unchanged | | 2026-10-25 | minor | G4: a double-click on a threaded port with the Selection tool breaks the thread there (`text.thread.break {id, port}`): the story flows into the frames before the break, the frames after it are left empty and stay threaded with each other when two or more; a double-click on a port with no thread does nothing; estimate unchanged | +| 2026-10-31 | minor | G1: Grid Repeat Options has a Preview checkbox (#2651); estimate unchanged. | | 2026-10-25 | minor | G1: a selected Grid Repeat shows handles: the one below the grid adds or removes rows, the one right of it columns, the one past its bottom-right corner both, and the sliders between the first two columns and rows set the horizontal and vertical spacing; each drag is one undo step and Escape cancels it (#2691); bounding-box handles keep priority where they meet; Radial Repeat handles remain; estimates unchanged. | | 2026-10-25 | minor | Floating panels open in OS windows of their own on the desktop (inside the window on the web and on Wayland); saved layouts from earlier versions convert to the shared docking layout (#4135); scores and estimates unchanged (estimated) | | 2026-10-24 | minor | Recolor Artwork › Assign: double-clicking a row's new color opens the Color Picker over the dialog; its OK sets the row's new color in the model it was picked in (a CMYK mix stays CMYK, RGB becomes CMYK in a CMYK document), Cancel leaves the row as it was. A Lab new color is kept, but a Lab color can't be typed yet: the picker's Lab fields work in RGB, as for fill and stroke (part of #4028; Lab entry, swatch libraries and the row menu remain). Scores and estimates unchanged. | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index a871b6747..b29ba2455 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1,6 +1,6 @@ # Parity with Adobe Illustrator -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G8: a gradient mesh from a file is budgeted, #3592; scores unchanged; previously: Path Precision Tools add-on: Shared Edge Merge Tool; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G1: Grid Repeat Options has a Preview checkbox, #2651; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026) ## Feature submission evidence @@ -19,6 +19,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | Date | Change | Summary | |---|---|---| +| 2026-10-31 | minor | Patterns: Grid Repeat Options has a Preview checkbox, as the public grid options page lists (#2651); scores unchanged. | | 2026-10-31 | minor | Hardening: a gradient mesh read from a file is budgeted at about a million quads, so a huge one no longer exhausts memory when drawn or exported (#3592); scores unchanged. | | 2026-10-30 | minor | File formats: `.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. | | 2026-10-30 | minor | File formats: `.ai`/EPS editing-data fidelity from an audit of a user's files: All Caps and Small Caps (#4708); placed files at their box's corner, kept where they draw closer to the page (#4664, #4723, #4742); pages cropped to the art, with `UserUnit` (#4658, #4727); gradient mesh corners with handle weights of 0 and the state written after a mesh (#4738, #4744, #4748); type on a path spaced as the file lays it out (#4743); synthetic regressions in `crates/eps`; evidence added; scores unchanged. | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 3313a18d2..6e6ed4b9b 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI and interaction parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (Path Precision Tools add-on: Shared Edge Merge Tool; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G1: Grid Repeat Options has a Preview checkbox, #2651; scores unchanged; previously: Path Precision Tools add-on: Shared Edge Merge Tool; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence @@ -274,6 +274,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po | Command search: Up and Down move through the results, Enter runs the highlighted one | done | #1798 (#1757) | | Pathfinder panel: a group selected alone counts its nested shapes, so an expanded stroked object keeps the Shape Modes enabled | done | #1642 (#1635) | | Selected Grid Repeat shows row, column and corner handles and spacing sliders; bounding-box handles take priority where they meet | done | #2691, `tools/src/repeat_grid.rs`; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html); drags preview and commit `object.repeat.options` as one undo step, Escape cancels; Radial Repeat has no handles yet | +| Grid Repeat Options (Object › Repeat › Options…) has a Preview checkbox: the repeat on the canvas follows the fields while the dialog is open, Cancel puts the original back without a history step, OK keeps the previewed values as one undo step | done | #2651; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html) lists the Preview option for the grid options only, so Radial and Mirror Repeat Options have none; `dialogs/command.rs` tests | | Selected Mirror Repeat shows a prominent axis with move and rotation handles; bounding-box handles take priority over the axis line | done | #2877, `tools/src/mirror.rs`; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-mirror-repeat-options.html) | | Object › Expand on radial, grid and mirror repeats makes groups of their instances | done | #1765 (#1756) | | Object › Expand… is dimmed while the selection has appearance attributes (effects, several fills or strokes, a fill or stroke with its own transparency or effects, a brush); its tooltip and the status bar say to use Object › Expand Appearance first, which leaves a stroked line with Zig Zag a stroked zig-zag line that Expand then outlines | done | #3089; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/manage-objects/arrange-objects/expand-objects.html); `dialogs/expand.rs` tests, engine `tests_objexpand`. `object.expand` (agents, scripts) still bakes effects in one step | @@ -375,6 +376,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development | Date | Change | Summary | |---|---|---| +| 2026-10-31 | minor | Grid Repeat Options has a Preview checkbox (#2651); scores and estimates unchanged | | 2026-10-31 | minor | Path Precision Tools add-on: the Shared Edge Merge Tool and Keep Shared Points Together; scores and estimates unchanged | | 2026-10-31 | minor | Path Precision Tools add-on: live Path Effects in Effect › Path Effects; scores and estimates unchanged | | 2026-10-31 | minor | Path Precision Tools add-on: the Corner Tool (editable corners, panel section) and Slide Anchor Along Path; scores and estimates unchanged |