duplicateSubtree static method

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

Implementation

static DuplicateSceneSubtreeResult duplicateSubtree(
  CanvasSceneDocument doc,
  ElementId 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 = <ElementId>[];
  final idMap = <ElementId, ElementId>{};

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

    return switch (n) {
      final TextNode node => node.copyWith(id: newId),
      final ImageNode node => node.copyWith(id: newId),
      final PathNode node => node.copyWith(id: newId),
      final IconNode node => node.copyWith(id: newId),
      final GroupNode node => node.copyWith(
        id: newId,
        children: [for (final child in node.children) clone(child)],
      ),
    };
  }

  // 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);
}