mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 05:24:44 +00:00
Merge pull request #7218 from dotCooCoo/fix/batch-fit-journal-index
Artboards: a batch's Fit to Artwork Bounds step journals the artboard it fits
This commit is contained in:
@@ -80,10 +80,12 @@ pub(super) fn batch(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
/// document's last artboard, the last one; a Paste in Place, in Front or in Back step that names
|
||||
/// `artboards` keeps them, and one that names neither, while the Artboard tool is chosen with
|
||||
/// several artboards selected, pastes on those. A Fit to Artwork Bounds step that names neither
|
||||
/// `index` nor `indices`, while the Artboard tool is chosen with several artboards selected when the
|
||||
/// step runs, gets those as its `indices`, which its journal entry records. Otherwise a step that
|
||||
/// resizes the active artboard ([`crate::FIT_ACTIVE_ARTBOARD`]) and names no `index` (nor
|
||||
/// `indices`, for Fit to Artwork Bounds) gets that artboard as its `index`.
|
||||
/// `index` nor `indices`, while the Artboard tool is chosen with two or more artboards selected when
|
||||
/// the step runs, gets those as its `indices`. Otherwise a step that resizes the active artboard
|
||||
/// ([`crate::FIT_ACTIVE_ARTBOARD`]) and names no `index` (nor `indices`, for Fit to Artwork Bounds)
|
||||
/// gets that artboard as its `index`. A Fit to Artwork Bounds step that still names neither gets
|
||||
/// `index` 0, the command's default. A Fit to Artwork Bounds step's journal entry records the
|
||||
/// `indices` or `index` it got.
|
||||
/// `grouped` gets each step's undo groups, as [`Session::batch_steps`] keeps them.
|
||||
fn run_steps(
|
||||
s: &mut Session,
|
||||
@@ -104,7 +106,8 @@ fn run_steps(
|
||||
if s.active().is_some() {
|
||||
s.begin_interaction(label)?;
|
||||
}
|
||||
let mut params = c.get("params").cloned().unwrap_or(json!({}));
|
||||
// `"params": null` reads as no params, so the artboards below can still be added.
|
||||
let mut params = c.get("params").filter(|v| !v.is_null()).cloned().unwrap_or(json!({}));
|
||||
let key = if crate::ON_ACTIVE_ARTBOARD.contains(&id.as_str()) {
|
||||
Some("artboard")
|
||||
} else {
|
||||
@@ -115,12 +118,9 @@ fn run_steps(
|
||||
let names_artboards = id.starts_with("edit.pasteIn")
|
||||
&& (params.get("artboards").is_some() || (params.get("artboard").is_none() && !crate::cmd::edit::artboard_tool_picks(s).is_empty()));
|
||||
// A Fit to Artwork Bounds step that names neither `index` nor `indices`, while the Artboard
|
||||
// tool is chosen with several artboards selected, fits those, as the command does on its own.
|
||||
let fit_picks = if id == "artboard.fitToArt" && params.get("index").is_none() && params.get("indices").is_none() {
|
||||
crate::cmd::edit::artboard_tool_picks(s)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
// tool is chosen with two or more artboards selected, fits those.
|
||||
let fit_unnamed = id == "artboard.fitToArt" && params.get("index").is_none() && params.get("indices").is_none();
|
||||
let fit_picks = if fit_unnamed { crate::cmd::edit::artboard_tool_picks(s) } else { Vec::new() };
|
||||
if !fit_picks.is_empty()
|
||||
&& let Some(o) = params.as_object_mut()
|
||||
{
|
||||
@@ -136,6 +136,15 @@ fn run_steps(
|
||||
{
|
||||
o.insert(key.into(), json!(usize::try_from(a).map_or(last, |a| a.min(last))));
|
||||
}
|
||||
// With neither, the step fits the command's default, the first artboard, named here so the
|
||||
// journal records it.
|
||||
if fit_unnamed
|
||||
&& params.get("index").is_none()
|
||||
&& params.get("indices").is_none()
|
||||
&& let Some(o) = params.as_object_mut()
|
||||
{
|
||||
o.insert("index".into(), json!(0));
|
||||
}
|
||||
// The documents with an undo group open, held so that the step's first edit in one copies it.
|
||||
let start = s.active().map(|d| d.uid);
|
||||
let held: Vec<(u64, Arc<Document>)> = s.docs.iter().filter(|st| st.undo_group.is_some()).map(|st| (st.uid, st.doc.clone())).collect();
|
||||
@@ -147,8 +156,9 @@ fn run_steps(
|
||||
grouped.push(step_groups(s, start, &held));
|
||||
// The step's journal entry records what it took from the preferences or the clock (a
|
||||
// save's date, a new document's), as a command of its own would, and the artboards a Fit to
|
||||
// Artwork Bounds step took from the Artboard tool.
|
||||
if (noted != params || !fit_picks.is_empty())
|
||||
// Artwork Bounds step took from the Artboard tool, the batch or the command's default, so a
|
||||
// replay does not fit the artboards the Artboard tool has selected then.
|
||||
if (noted != params || fit_unnamed)
|
||||
&& let Some(c) = c.as_object_mut()
|
||||
{
|
||||
c.insert("params".into(), noted);
|
||||
|
||||
@@ -108,7 +108,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Batch",
|
||||
[],
|
||||
None,
|
||||
"{label?, commands: [{command, params}], artboard?: index (the active artboard; the app passes it: each edit.pasteInPlace, edit.pasteInFront, edit.pasteInBack, object.align, object.distribute, object.distributeSpacing, select.allOnArtboard or symbol.edit step that names none gets it, and each artboard.fitToArt or artboard.fitToSelection step that names no index gets it as index, or the last artboard of a document with fewer; an artboard.fitToArt step that names neither index nor indices, while the Artboard tool is chosen with several artboards selected when the step runs, gets those as indices instead, and the journal records them)} run several commands as ONE undo step (one in each document they edit: steps may open, switch, close or revert documents); stops at the first error and rolls everything back, documents included",
|
||||
"{label?, commands: [{command, params}], artboard?: index (the active artboard; the app passes it: each edit.pasteInPlace, edit.pasteInFront, edit.pasteInBack, object.align, object.distribute, object.distributeSpacing, select.allOnArtboard or symbol.edit step that names none gets it, and each artboard.fitToArt or artboard.fitToSelection step that names no index gets it as index, or the last artboard of a document with fewer; an artboard.fitToArt step that names neither index nor indices, while the Artboard tool is chosen with two or more artboards selected when the step runs, gets those as indices instead; the journal records the indices or index an artboard.fitToArt step got)} run several commands as ONE undo step (one in each document they edit: steps may open, switch, close or revert documents); stops at the first error and rolls everything back, documents included",
|
||||
has_doc,
|
||||
batch::batch
|
||||
),
|
||||
|
||||
@@ -273,6 +273,58 @@ fn a_batch_fit_selection_replays_but_an_action_uses_the_current_selection() {
|
||||
assert_eq!(boards(&a), [was[0], fitted[1], fitted[2]], "the action fits the artboards selected when it plays");
|
||||
}
|
||||
|
||||
/// The journal records the `index` a batch's Fit to Artwork Bounds step took from the batch's
|
||||
/// `artboard`, or the command's default (the first artboard) when the batch names none, so a replay
|
||||
/// fits that artboard even when the Artboard tool then has several selected.
|
||||
#[test]
|
||||
fn a_batch_fit_step_records_the_batchs_artboard() {
|
||||
let fitted = three_fitted_artboards();
|
||||
let boards = |s: &Session| s.doc().unwrap().doc.artboards.iter().map(|a| a.rect).collect::<Vec<_>>();
|
||||
let steps = json!([{"command": "artboard.fitToArt", "params": {}}]);
|
||||
for (batch, index, picks) in [(json!({"artboard": 1, "commands": steps}), 1, json!([0, 2])), (json!({"commands": steps}), 0, json!([1, 2]))] {
|
||||
let mut s = Session::new();
|
||||
let was = three_artboards_with_art(&mut s);
|
||||
let start = s.journal.len();
|
||||
s.execute("command.batch", &batch).unwrap();
|
||||
let mut want = was.clone();
|
||||
want[index] = fitted[index];
|
||||
assert_eq!(boards(&s), want, "{batch}");
|
||||
let (id, p) = s.journal.last().unwrap().clone();
|
||||
assert_eq!((id.as_str(), &p["commands"][0]["params"]), ("command.batch", &json!({ "index": index })));
|
||||
let mut r = Session::new();
|
||||
for (n, (id, p)) in s.journal.iter().enumerate() {
|
||||
if n == start {
|
||||
r.select_tool("artboard", crate::tooling::ViewInfo::default()).unwrap();
|
||||
r.set_tool_option("selected", &picks);
|
||||
}
|
||||
r.execute(id, p).unwrap();
|
||||
}
|
||||
assert_eq!(boards(&r), want, "{batch}: the replay fits artboard {index}, not the selected ones");
|
||||
assert_eq!(s.journal_for_action(start), [("command.batch".to_string(), json!({"commands": steps}))]);
|
||||
}
|
||||
}
|
||||
|
||||
/// A batch step with `"params": null` gets the Artboard tool's selected artboards, or else the
|
||||
/// batch's `artboard`, as a step without params does.
|
||||
#[test]
|
||||
fn a_batch_fit_step_with_null_params_gets_the_artboards() {
|
||||
let fitted = three_fitted_artboards();
|
||||
let boards = |s: &Session| s.doc().unwrap().doc.artboards.iter().map(|a| a.rect).collect::<Vec<_>>();
|
||||
let steps = json!([{"command": "artboard.fitToArt", "params": null}]);
|
||||
let mut s = Session::new();
|
||||
let was = three_artboards_with_art(&mut s);
|
||||
s.select_tool("artboard", crate::tooling::ViewInfo::default()).unwrap();
|
||||
s.set_tool_option("selected", &json!([2, 0]));
|
||||
s.execute("command.batch", &json!({"artboard": 1, "commands": steps})).unwrap();
|
||||
assert_eq!(boards(&s), [fitted[0], was[1], fitted[2]], "the selected artboards");
|
||||
let (_, p) = s.journal.last().unwrap().clone();
|
||||
assert_eq!(p["commands"][0]["params"], json!({"indices": [0, 2]}));
|
||||
let mut s = Session::new();
|
||||
three_artboards_with_art(&mut s);
|
||||
s.execute("command.batch", &json!({"artboard": 1, "commands": steps})).unwrap();
|
||||
assert_eq!(boards(&s), [was[0], fitted[1], was[2]], "the batch's artboard");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_save_records_its_date_and_replays_it() {
|
||||
let dir = vectorcraft_testkit::TempDir::new("journal-save");
|
||||
|
||||
+6
-5
@@ -5607,11 +5607,12 @@ the selected artboards (those the Artboard tool has selected, else the active on
|
||||
Artboards panel menu's Fit to Artwork Bounds also passes the selected artboards. A batch passes its `artboard` on to
|
||||
each of their steps that names no `index`, as the step's `index`. When the document a step runs in has no artboard at
|
||||
that index, the step gets that document's last artboard. An `artboard.fitToArt` step that names neither `index` nor
|
||||
`indices`, while the Artboard tool is chosen with several artboards selected when the step runs, gets those artboards
|
||||
as its `indices` instead, and the batch's journal entry records them, so a replay of the journal fits the same
|
||||
artboards. A step's own `index` or `indices` is kept. An action recorded in the Actions panel leaves `index` and
|
||||
`indices` out of these steps: when the action plays, Fit to Artwork Bounds resizes the artboards the Artboard tool then
|
||||
has selected (two or more), else the active artboard, and Fit to Selected Art resizes the active artboard.
|
||||
`indices`, while the Artboard tool is chosen with two or more artboards selected when the step runs, gets those
|
||||
artboards as its `indices` instead. The batch's journal entry records the `indices` or `index` such a step got, so a
|
||||
replay of the journal fits the same artboards. A step's own `index` or `indices` is kept. An action recorded in the
|
||||
Actions panel leaves `index` and `indices` out of these steps: when the action plays, Fit to Artwork Bounds resizes the
|
||||
artboards the Artboard tool then has selected (two or more), else the active artboard, and Fit to Selected Art resizes
|
||||
the active artboard.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"object.align","params":{"horizontal":"left","to":"artboard","artboard":1}}}
|
||||
|
||||
@@ -1177,7 +1177,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
Choosing the Artboard tool (Shift+O, its toolbar button, Edit Artboards in Properties or Document Setup, or the Artboards panel menu's Artboard Options…) starts it on the active artboard: the canvas outlines that artboard, and Delete, Cut, Copy, the Transform panel and Artboard Options act on it.
|
||||
Object › Artboards › Fit to Selected Art resizes the active artboard (`artboard.fitToSelection {index}`). It used to resize artboard 1 whichever artboard was active. When an action plays, its Fit to Selected Art steps that name no artboard resize the artboard that is active then, and its Fit to Artwork Bounds steps resize the artboards selected then.
|
||||
Object › Artboards › Fit to Artwork Bounds and the Artboards panel menu's Fit to Artwork Bounds fit each selected artboard (those the Artboard tool has selected, else the active one) to the art on it, as the public [Use the artboard context menu](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/use-artboard-context-menu.html) page describes (`artboard.fitToArt {indices}`). An object partly on an artboard counts for each artboard it overlaps, and an artboard with no art on it keeps its size and place. They used to fit the active artboard to all the art in the document.
|
||||
A Fit to Artwork Bounds step of a batch run from the app (`command.batch`), including an action played from the Actions panel, fits each artboard the Artboard tool has selected when the step runs, as the command does on its own and as the [help page](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/use-artboard-context-menu.html) describes. A step's own `index` or `indices` is kept, and the journal records the artboards the step fitted, so a replay fits the same ones. The step used to fit only the active artboard.
|
||||
A Fit to Artwork Bounds step of a batch run from the app (`command.batch`), including an action played from the Actions panel, fits each artboard the Artboard tool has selected when the step runs, as the [help page](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/use-artboard-context-menu.html) describes, when two or more are selected; with fewer, it fits the active artboard. A step's own `index` or `indices` is kept, and the journal records the artboards the step fitted, so a replay fits the same ones. The step used to fit only the active artboard.
|
||||
The Artboards panel menu's Duplicate Artboards copies each selected artboard (those the Artboard tool has selected, else the active one) with its art, in one undo step, and the copies are then selected, as the public [Use the artboard context menu](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/use-artboard-context-menu.html) page describes. With the Artboard tool, Copy and Cut take every selected artboard with its art, as the public [Cut, copy, and paste artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/cut-copy-and-paste-artboards.html) page describes, and Paste brings them all back in one undo step (`artboard.duplicate`, `artboard.copy` and `artboard.cut` with `indices`). The copies keep their places relative to each other, together right of the last artboard. All three used to act on the active artboard only.
|
||||
With the Artboard tool selecting several artboards, Paste in Place, Paste in Front and Paste in Back put a copy of the copied art on each, where it was on the artboard it was copied from, in one undo step, as the public [Cut, copy, and paste artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/cut-copy-and-paste-artboards.html) page describes (`artboards` on the three commands). Art copied from no artboard keeps its place on the first selected artboard and its offset to it on the others, and an `artboard` or `artboards` index the document does not have is an error; these rules are VectorCraft's own. The three commands used to paste one copy, on the active artboard, and an `artboard` index the document did not have pasted the art where it was.
|
||||
A replay of the journal repeats a Cut or Copy made with the Artboard tool on the artboards it took (`edit.cut` and `edit.copy` record `selectedArtboards` and `art`), whichever tool is chosen in the new session and whatever is selected there. A recorded action follows the tool chosen when it plays. The replay used to act on the objects selected then, or stop with nothing selected. This rule is VectorCraft's own.
|
||||
|
||||
Reference in New Issue
Block a user