mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 09:59:47 -06:00
Symbols: Replace Symbol clears the old symbol's dynamic overrides
An override on a dynamic symbol instance names an object of its symbol's art by path. Replace Symbol kept every override whose path the new symbol's art also had, so a change made to one object of the old symbol was applied to an unrelated object of the new one. An instance given a different symbol now drops its overrides; an instance replaced with its own symbol keeps them. Undo brings them back.
This commit is contained in:
@@ -247,7 +247,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Replace Symbol",
|
||||
[],
|
||||
None,
|
||||
"{name, ids?|id?} swap the symbol of the selected instances (they keep their appearance changes to the objects at paths a dynamic new symbol's art has) → {ids}",
|
||||
"{name, ids?|id?} swap the symbol of the selected instances (an instance given a different symbol drops its appearance changes; one given its own symbol keeps them) → {ids}",
|
||||
has_selection,
|
||||
symbol_replace
|
||||
),
|
||||
|
||||
@@ -590,14 +590,19 @@ pub(super) fn symbol_replace(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
s.edit("Replace Symbol", |d, _| {
|
||||
let new = natural_size(d, &name);
|
||||
for id in &ids {
|
||||
let old = match d.node(*id).map(|n| &n.kind) {
|
||||
Some(NodeKind::SymbolInstance { symbol, .. }) => natural_size(d, symbol),
|
||||
let (old, other) = match d.node(*id).map(|n| &n.kind) {
|
||||
Some(NodeKind::SymbolInstance { symbol, .. }) => (natural_size(d, symbol), *symbol != name),
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(NodeKind::SymbolInstance { symbol, xf }) = d.node_mut(*id).map(|n| &mut n.kind) {
|
||||
let Some(n) = d.node_mut(*id) else { continue };
|
||||
if let NodeKind::SymbolInstance { symbol, xf } = &mut n.kind {
|
||||
*symbol = name.clone();
|
||||
*xf *= Affine::scale_non_uniform(new.0 / old.0, new.1 / old.1);
|
||||
}
|
||||
// An override's path names an object of the old symbol's art.
|
||||
if other && !n.extras().symbol_overrides.is_empty() {
|
||||
n.extras_mut().symbol_overrides.clear();
|
||||
}
|
||||
}
|
||||
fit_overrides(d, &ids);
|
||||
Ok(())
|
||||
|
||||
@@ -111,6 +111,28 @@ fn redefining_the_symbol_keeps_the_changes_whose_objects_remain() {
|
||||
assert_eq!(overrides(&s, inst), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replace_symbol_drops_the_changes_made_for_the_old_symbol() {
|
||||
let mut s = session();
|
||||
// "Other": two blue squares in the same places, the instance taken off the canvas.
|
||||
let (a, b) = (square(&mut s, 10.0), square(&mut s, 60.0));
|
||||
let first = id(&s.execute("symbol.new", &json!({"name": "Other", "ids": [a.0, b.0], "type": "dynamic"})).unwrap());
|
||||
s.execute("edit.clear", &json!({"ids": [first.0]})).unwrap();
|
||||
let inst = pair(&mut s, "dynamic");
|
||||
s.execute("symbol.instance.setAppearance", &json!({"id": inst.0, "path": [1], "fill": "#ff0000"})).unwrap();
|
||||
// The same symbol: nothing changes.
|
||||
s.execute("symbol.replace", &json!({"name": "Pair", "ids": [inst.0]})).unwrap();
|
||||
assert_eq!(overrides(&s, inst), 1);
|
||||
assert_eq!(pixel(&s, 80, 30), RED);
|
||||
s.execute("symbol.replace", &json!({"name": "Other", "ids": [inst.0]})).unwrap();
|
||||
assert_eq!(overrides(&s, inst), 0, "the change named an object of Pair's art");
|
||||
assert_eq!(pixel(&s, 80, 30), BLUE, "Other's art draws as the symbol has it");
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert!(matches!(&node(&s, inst).kind, NodeKind::SymbolInstance { symbol, .. } if symbol == "Pair"));
|
||||
assert_eq!(overrides(&s, inst), 1);
|
||||
assert_eq!(pixel(&s, 80, 30), RED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_and_static_drop_the_changes_and_undo_brings_them_back() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user