Symbols: Edit Symbol opens the symbol in its own editing mode, and every instance follows each edit

Squashed from #5064 onto main.
This commit is contained in:
dotCooCoo
2026-11-01 11:09:03 +07:00
committed by bflatastic
parent 5716a996ab
commit ff036557fb
53 changed files with 1353 additions and 71 deletions
+5
View File
@@ -1170,6 +1170,11 @@ impl SelectionTool {
if cx.doc.node(top).is_some_and(|n| matches!(n.kind, vectorcraft_doc::NodeKind::Text(_))) {
return vec![Action::SwitchTool("type".into())];
}
// A symbol instance: its symbol is edited in symbol editing mode, except one inside
// the art of a symbol already being edited.
if cx.doc.node(top).is_some_and(|n| matches!(n.kind, vectorcraft_doc::NodeKind::SymbolInstance { .. })) {
return if cx.doc.symbol_edit.is_some() { vec![] } else { vec![Action::Exec("symbol.edit".into(), json!({ "id": top.0 }))] };
}
// An object that [`isolates`], not the one already isolated.
if cx.double_click_isolate && cx.doc.node(top).is_some_and(|n| isolates(&n.kind)) && cx.isolation != Some(top) {
return vec![Action::Exec("object.isolate".into(), json!({ "id": top.0 }))];
+34
View File
@@ -1964,3 +1964,37 @@ fn the_preview_memo_measures_once_per_revision_selection_and_preview_bounds() {
let c = cx(&moved, &s, &p);
assert_eq!(selection_bounds(&c).map(|r| r.x1), Some(250.0));
}
/// A double-click on a symbol instance edits its symbol in symbol editing mode (`symbol.edit`), and
/// one outside the symbol's art in that mode leaves it, as it leaves isolation mode. An instance
/// inside the art of the symbol being edited is not entered.
#[test]
fn a_double_click_on_a_symbol_instance_edits_its_symbol() {
use vectorcraft_doc::{Appearance, Node, NodeKind, Symbol, SymbolEdit};
let (mut d, rect) = doc_with_rect();
let art = Node::path(NodeId(0), vectorcraft_geom::shapes::rectangle(Rect::new(-10.0, -10.0, 10.0, 10.0)), Appearance::default_art());
d.symbols.push(Symbol { dynamic: false, name: "Dot".into(), art: std::sync::Arc::new(art) });
let (l, id) = (d.layers[0].id, d.alloc_id());
let xf = Affine::translate((300.0, 300.0)) * Affine::scale(2.0);
d.insert(Some(l), usize::MAX, Node::new(id, NodeKind::SymbolInstance { symbol: "Dot".into(), xf })).unwrap();
let (s, p) = (Selection::default(), paint());
let double = |d: &vectorcraft_doc::Document, isolation: Option<NodeId>, x: f64, y: f64| {
let c = ToolContext { isolation, ..cx(d, &s, &p) };
SelectionTool::default().pointer(&c, &ev(PointerKind::DoubleClick, x, y))
};
assert_eq!(double(&d, None, 300.0, 300.0), vec![Action::Exec("symbol.edit".into(), json!({"id": id.0}))]);
assert_eq!(double(&d, None, 150.0, 150.0), vec![Action::Exec("object.isolate".into(), json!({"id": rect.0}))]);
// In symbol editing mode, with the instance on the editing layer.
d.symbol_edit = Some(SymbolEdit {
symbol: "Other".into(),
layer: l,
instance: None,
xf: Affine::IDENTITY,
group: false,
changed: false,
back_isolation: None,
back_layer: None,
});
assert_eq!(double(&d, Some(l), 300.0, 300.0), vec![]);
assert_eq!(double(&d, Some(l), 450.0, 50.0), vec![Action::Exec("object.exitIsolation".into(), json!({}))]);
}