duplicateSubtree static method

DuplicateSceneSubtreeResult duplicateSubtree(
  1. CanvasSceneDocument doc,
  2. NodeId id, {
  3. Vec2 shift = const Vec2(16, 16),
  4. IdGenerator? idGen,
})

Implementation

static DuplicateSceneSubtreeResult duplicateSubtree(
  CanvasSceneDocument doc,
  NodeId id, {
  Vec2 shift = const Vec2(16, 16),
  IdGenerator? idGen,
}) {
  final node = findById(doc, id);
  if (node == null) {
    return (doc: doc, idMap: const {}, createdIds: const [], primaryId: null);
  }

  final pr = findParentOf(doc, id);
  if (pr == null) {
    return (doc: doc, idMap: const {}, createdIds: const [], primaryId: null);
  }

  final gen = idGen ?? _defaultIdGen();

  final created = <NodeId>[];
  final idMap = <NodeId, NodeId>{};

  Node clone(Node n) {
    final newId = gen(n.id);
    idMap[n.id] = newId;
    created.add(newId);

    final kids = n.childrenOrEmpty;
    final clonedKids = kids.isEmpty
        ? const <Node>[]
        : [for (final c in kids) clone(c)];

    return n.withId(newId).withChildren(clonedKids);
  }

  // Clone subtree.
  var cloned = clone(node);

  // Shift root position only.
  final xf0 = cloned.xf;
  cloned = cloned.withXf(xf0.copyWith(position: xf0.position + shift));

  // Insert immediately after original.
  // Later siblings paint above earlier siblings, so the duplicate appears above.
  final insertIndex = pr.index + 1;

  CanvasSceneDocument out;
  if (pr.parent == null) {
    final kids = [...doc.children];
    kids.insert(_clampInsertIndex(insertIndex, kids.length), cloned);
    out = doc.copyWith(children: kids);
  } else {
    final kids = [...pr.parent!.childrenOrEmpty];
    kids.insert(_clampInsertIndex(insertIndex, kids.length), cloned);
    out = _replaceChildren(doc, pr.parent, kids);
  }

  return (doc: out, idMap: idMap, createdIds: created, primaryId: cloned.id);
}