mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 03:39:50 +00:00
Perspective: reapplying the current grid preset adds no undo step
perspective.grid.preset stored the preset's grid every time, so applying the preset the document already holds recorded an undo step that changed nothing. It now compares the new grid with the stored one, as perspective.grid.set does through grid_kept, and records a step only when the grid changes. When only whether the grid shows or its active plane differs, the preset sets them without an undo step, as perspective.grid.show and perspective.plane.set do.
This commit is contained in:
@@ -26,7 +26,7 @@ pub use perspective::perspective_click;
|
||||
use perspective::{
|
||||
attach, edit_text, grid_set, grid_show, persp_draw, persp_move, persp_nudge, persp_transform, plane_match, plane_move, plane_set, release,
|
||||
};
|
||||
pub(crate) use perspective::{finish_edit_text, grid_of, perspective_rewrite, silent, store_grid, transform_again};
|
||||
pub(crate) use perspective::{finish_edit_text, grid_kept, grid_of, perspective_rewrite, silent, store_grid, transform_again};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
@@ -107,7 +107,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Perspective Grid Preset",
|
||||
[],
|
||||
None,
|
||||
"{kind: 1|2|3 (its normal view) | name: a preset (see perspective.presets.list)} reset the grid to the preset fitted to the first artboard (and show it; attached objects stay attached)",
|
||||
"{kind: 1|2|3 (its normal view) | name: a preset (see perspective.presets.list)} reset the grid to the preset fitted to the first artboard (and show it; attached objects stay attached). No undo step when the document already holds that grid; when only whether it shows or its active plane differs, those are set without one",
|
||||
has_doc,
|
||||
super::perspgrid::grid_preset
|
||||
),
|
||||
|
||||
@@ -18,7 +18,7 @@ pub(crate) fn store_grid(d: &mut Document, g: &PerspectiveGrid) {
|
||||
|
||||
/// Whether `d` holds `g` as [`store_grid`] writes it (with `attached` cleared): then storing `g`
|
||||
/// leaves `d` as it is.
|
||||
fn grid_kept(d: &Document, g: &PerspectiveGrid) -> bool {
|
||||
pub(crate) fn grid_kept(d: &Document, g: &PerspectiveGrid) -> bool {
|
||||
let mut g = g.clone();
|
||||
g.attached.clear();
|
||||
serde_json::to_value(&g).is_ok_and(|v| d.unknown.get(persp::DOC_KEY) == Some(&v))
|
||||
|
||||
@@ -19,7 +19,7 @@ use vectorcraft_tools::distort::perspective::widget::{WidgetCorner, WidgetPlace}
|
||||
use vectorcraft_tools::distort::perspective::{GridDefinition, PerspectiveGrid};
|
||||
use vectorcraft_tools::{PointerEvent, PointerKind, ToolKey};
|
||||
|
||||
use super::distortcmds::{grid_of, silent, store_grid};
|
||||
use super::distortcmds::{grid_kept, grid_of, silent, store_grid};
|
||||
use super::{CommandSpec, EngineError, Result, Session, always, bad, bool_or, cmd, has_doc, str_param, switch_or, unique_name};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
@@ -261,10 +261,21 @@ pub(crate) fn grid_preset(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let g = s.perspective_preset_grid(&name, ab).ok_or_else(|| bad(C, format!("no perspective grid preset named `{name}`")))?;
|
||||
// The objects stay attached and the view options stay as they were.
|
||||
let g = PerspectiveGrid { attached: old.attached, locked: old.locked, lock_station: old.lock_station, snap: old.snap, rulers: old.rulers, ..g };
|
||||
s.edit("Perspective Grid Preset", |d, _| {
|
||||
store_grid(d, &g);
|
||||
Ok(())
|
||||
})?;
|
||||
// When the document holds this grid already, apart from whether it shows and its active plane,
|
||||
// the preset only sets those two, without an undo step (as perspective.grid.show and
|
||||
// perspective.plane.set do).
|
||||
let view = PerspectiveGrid { visible: old.visible, plane: old.plane, ..g.clone() };
|
||||
if !grid_kept(&s.doc()?.doc, &view) {
|
||||
s.edit("Perspective Grid Preset", |d, _| {
|
||||
store_grid(d, &g);
|
||||
Ok(())
|
||||
})?;
|
||||
} else if (old.visible, old.plane) != (g.visible, g.plane) {
|
||||
silent(s, |cur| {
|
||||
cur.visible = g.visible;
|
||||
cur.plane = g.plane;
|
||||
})?;
|
||||
}
|
||||
Ok(g.definition_json())
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,42 @@ fn built_in_views_apply_by_name_or_type() {
|
||||
assert!(s.execute("perspective.grid.preset", &json!({})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reapplying_the_current_grid_preset_records_no_undo_step() {
|
||||
let mut s = session();
|
||||
// On a document with no grid defined, a preset defines and shows one: an undo step.
|
||||
run(&mut s, "perspective.grid.preset", json!({"kind": 2}));
|
||||
assert_eq!(undo_len(&s), 1);
|
||||
let g = grid(&s);
|
||||
// The same preset again, by type or by name, leaves the grid as it is: no undo step.
|
||||
run(&mut s, "perspective.grid.preset", json!({"kind": 2}));
|
||||
run(&mut s, "perspective.grid.preset", json!({"name": "[2P-Normal View]"}));
|
||||
assert_eq!((grid(&s), undo_len(&s)), (g.clone(), 1));
|
||||
// Another active plane, or a hidden grid, is set back without an undo step.
|
||||
run(&mut s, "perspective.plane.set", json!({"plane": "ground"}));
|
||||
run(&mut s, "perspective.grid.show", json!({"visible": false}));
|
||||
assert_ne!(grid(&s), g);
|
||||
run(&mut s, "perspective.grid.preset", json!({"kind": 2}));
|
||||
assert_eq!((grid(&s), undo_len(&s)), (g, 1));
|
||||
// Another preset changes the grid.
|
||||
run(&mut s, "perspective.grid.preset", json!({"kind": 3}));
|
||||
assert_eq!((grid(&s).kind, undo_len(&s)), (3, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn define_grid_with_unchanged_fields_records_no_undo_step() {
|
||||
let mut s = session();
|
||||
// On a document with no grid defined, `perspective.grid.set` stores the grid: an undo step,
|
||||
// and the grid no longer follows the first artboard.
|
||||
run(&mut s, "perspective.grid.set", json!({}));
|
||||
assert_eq!((run(&mut s, "perspective.grid.get", json!({}))["defined"].clone(), undo_len(&s)), (json!(true), 1));
|
||||
// The stored grid's own values leave it as it is: no undo step.
|
||||
let g = grid(&s);
|
||||
run(&mut s, "perspective.grid.set", json!({}));
|
||||
run(&mut s, "perspective.grid.set", json!({"cell": g.cell, "horizon": g.horizon}));
|
||||
assert_eq!((grid(&s), undo_len(&s)), (g, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn presets_save_rename_delete_and_protect_the_built_in_ones() {
|
||||
let mut s = session();
|
||||
|
||||
+4
-2
@@ -10028,8 +10028,10 @@ Perspective grid presets: `perspective.presets.list` lists the built-in views (`
|
||||
`[1P-High View]`, `[2P-Normal View]`, `[2P-Low View]`, `[2P-High View]`, `[3P-Normal View]`, `[3P-Low View]`;
|
||||
`builtIn: true`, fitted to the first artboard, protected), then the saved ones, each with its Define Grid fields.
|
||||
`perspective.grid.preset {name}` (or `{kind}`, that type's normal view) resets the grid to one, fitted to the first
|
||||
artboard. `perspective.presets.save {name?, newName?, preset?, …fields}` saves a preset: just `{name}` saves the
|
||||
document's grid (View → Perspective Grid → Save Grid as Preset). `perspective.presets.delete {name}`,
|
||||
artboard. It adds no undo step when the document already holds that grid. When only whether the grid shows or its
|
||||
active plane differs, it sets them without an undo step, as `perspective.grid.show` and `perspective.plane.set` do.
|
||||
`perspective.presets.save {name?, newName?, preset?, …fields}` saves a preset: just `{name}` saves the document's grid
|
||||
(View → Perspective Grid → Save Grid as Preset). `perspective.presets.delete {name}`,
|
||||
`perspective.presets.export {names?, path?}` (a `.vcperspective` JSON file; without `path` it returns `data`) and
|
||||
`perspective.presets.import {path? | data? | dataBase64?, replace?}` manage them. Saved presets are kept with the
|
||||
preferences, and a grid whose fields are a preset's is named after it (`name`).
|
||||
|
||||
Reference in New Issue
Block a user