restore method

void restore(
  1. SceneSnapshot state
)

Write state back into this document — the undo door's other half. Nodes are REVIVED, not replaced: a live node with the snapshot's name and kind gets the state copied into it and keeps its object identity, so fx writers (an Effect, a bound motion), widget keys and anything else holding the node keep working across an undo. Only nodes the snapshot has and the document lost come back as fresh copies.

Implementation

void restore(SceneSnapshot state) {
  edit(() {
    params
      ..clear()
      ..addAll(state._params);
    var live = {for (var (n, _) in walk()) n.name: n};
    SceneNode revive(SceneNode snap) {
      var into = live[snap.name];
      if (into == null ||
          into.runtimeType != snap.runtimeType ||
          (into is KindNode && into.kind != (snap as KindNode).kind) ||
          (into is ExternalNode &&
              into.entry != (snap as ExternalNode).entry) ||
          (into is SceneRefNode &&
              into.sceneClassName != (snap as SceneRefNode).sceneClassName)) {
        return deepCopyNode(snap);
      }
      switch ((into, snap)) {
        case (FrameNode i, FrameNode s):
          i
            ..repeated = s.repeated
            ..children.clear()
            ..children.addAll([for (var c in s.children) revive(c)]);
        case (ExternalNode i, ExternalNode s):
          i
            ..declared = s.declared
            ..builder = s.builder
            ..args.clear();
          i.args.addAll(s.args);
        case (SceneRefNode i, SceneRefNode s):
          i
            ..declared = s.declared
            ..tokensArg = s.tokensArg
            ..args.clear();
          i.args.addAll(s.args);
        case (KindNode i, KindNode s):
          i.children
            ..clear()
            ..addAll([for (var c in s.children) revive(c)]);
        case (TextNode(), TextNode()) || (ShapeNode(), ShapeNode()):
          break;
        default:
          throw StateError('unreachable: kinds matched above');
      }
      for (var p in scenePropsOf(snap)) {
        p.write(into, p.read(snap));
      }
      into.bindings
        ..clear()
        ..addAll(snap.bindings);
      return into;
    }

    revive(state._root);
    // A node the snapshot brought back is a NEW object, and a repeat's
    // closure reads the cells of the one it was built for. Rebound here
    // rather than left to whoever notices.
    bindRepeats(this);
  });
}