diff static method

UiDiff diff(
  1. UiSnapshot? old,
  2. UiSnapshot newSnap
)

Produce a diff from old to newSnap.

Implementation

static UiDiff diff(UiSnapshot? old, UiSnapshot newSnap) {
  if (old == null || old.screen != newSnap.screen) {
    return UiDiff(
      screen: newSnap.screen,
      oldCapturedAt: null,
      newCapturedAt: newSnap.capturedAt,
      added: [
        for (final n in newSnap.nodes)
          UiNodeAdded(node: n, path: newSnap.paths[n.stableId] ?? ''),
      ],
      removed: const [],
      updated: const [],
      moved: const [],
      visibilityChanged: const [],
    );
  }

  final oldById = {for (final n in old.nodes) n.stableId: n};
  final newById = {for (final n in newSnap.nodes) n.stableId: n};

  final added = <UiNodeAdded>[];
  final removed = <String>[];
  final updated = <UiNodeUpdate>[];
  final moved = <UiNodeMove>[];
  final visibilityChanged = <UiVisibilityChange>[];

  for (final entry in newById.entries) {
    final id = entry.key;
    final n = entry.value;
    final o = oldById[id];

    if (o == null) {
      added.add(UiNodeAdded(node: n, path: newSnap.paths[id] ?? ''));
      continue;
    }

    final roleChanged = o.role != n.role;
    final textChanged = o.text != n.text;
    final boundsChanged = !_rectEqual(o.bounds, n.bounds);
    final scrollableChanged = o.scrollable != n.scrollable;
    final hasActionChanged = o.hasAction != n.hasAction;
    final actionsChanged = !_listEqual(o.actions, n.actions);
    final valueChanged = o.value != n.value;
    final checkedChanged = o.checked != n.checked;
    final selectedChanged = o.selected != n.selected;
    final disabledChanged = o.disabled != n.disabled;
    final placeholderChanged = o.placeholder != n.placeholder;
    final rangeChanged = o.range != n.range;
    final normChanged =
        textChanged || o.wireNormalizedText != n.wireNormalizedText;
    final subtreeChanged = (o.wireSubtreeText ?? o.wireNormalizedText) !=
        (n.wireSubtreeText ?? n.wireNormalizedText);
    final layerChanged = o.layer != n.layer;
    final editableChanged = o.editable != n.editable;
    final secureChanged = o.secure != n.secure;
    final parentChanged = o.parentId != n.parentId;
    final visibilityFlipped = o.visible != n.visible;
    final fractionMoved =
        (o.wireVisibleFraction - n.wireVisibleFraction).abs() >
            kVisibleFractionDelta + 1e-9;

    final nonVisibilityChange = roleChanged ||
        textChanged ||
        boundsChanged ||
        scrollableChanged ||
        hasActionChanged ||
        actionsChanged ||
        valueChanged ||
        checkedChanged ||
        selectedChanged ||
        disabledChanged ||
        placeholderChanged ||
        rangeChanged ||
        normChanged ||
        subtreeChanged ||
        layerChanged ||
        editableChanged ||
        secureChanged ||
        parentChanged;
    final fractionChanged = visibilityFlipped || fractionMoved;

    if (fractionChanged && !nonVisibilityChange) {
      visibilityChanged.add(UiVisibilityChange(
        stableId: id,
        visible: n.visible,
        visibleFraction: n.wireVisibleFraction,
      ));
    } else if (nonVisibilityChange) {
      updated.add(UiNodeUpdate(
        stableId: id,
        role: roleChanged ? n.role : null,
        text: textChanged ? n.text : null,
        bounds: boundsChanged ? _rectToList(n.bounds) : null,
        scrollable: scrollableChanged ? n.scrollable : null,
        hasAction: hasActionChanged ? n.hasAction : null,
        visible: visibilityFlipped ? n.visible : null,
        actions: actionsChanged ? n.actions : null,
        value: valueChanged ? n.value : null,
        checked: checkedChanged ? n.checked : null,
        selected: selectedChanged ? n.selected : null,
        disabledChanged: disabledChanged,
        disabled: n.disabled,
        placeholder: placeholderChanged ? n.placeholder : null,
        rangeChanged: rangeChanged,
        range: n.range,
        normalizedText: normChanged ? n.wireNormalizedText : null,
        subtreeText: subtreeChanged
            ? (n.wireSubtreeText ?? n.wireNormalizedText)
            : null,
        layer: layerChanged ? n.layer : null,
        editable: editableChanged ? n.editable : null,
        secure: secureChanged ? n.secure : null,
        visibleFraction: fractionChanged ? n.wireVisibleFraction : null,
        parentId: parentChanged ? n.parentId : null,
      ));
    }

    final oldPath = old.paths[id] ?? '';
    final newPath = newSnap.paths[id] ?? '';
    if (oldPath != newPath) {
      moved.add(UiNodeMove(
        stableId: id,
        oldPath: oldPath,
        newPath: newPath,
      ));
    }
  }

  for (final id in oldById.keys) {
    if (!newById.containsKey(id)) removed.add(id);
  }

  return UiDiff(
    screen: newSnap.screen,
    oldCapturedAt: old.capturedAt,
    newCapturedAt: newSnap.capturedAt,
    added: added,
    removed: removed,
    updated: updated,
    moved: moved,
    visibilityChanged: visibilityChanged,
  );
}