setSceneProperty function

void setSceneProperty(
  1. SceneNode node,
  2. String prop,
  3. Object? value
)

Write one authored property by the name a SceneNode.bindings entry keys it under. The one place that maps a property name to a slot, so an argument and a repeated item's field land the same way.

Implementation

void setSceneProperty(SceneNode node, String prop, Object? value) {
  switch (resolveSceneKey(node, prop)) {
    case ScenePropertyKey(:var prop):
      prop.write(node, switch (prop.kind) {
        // A number filling a text slot is ordinary in a repeated row — a
        // quantity is a number and reads as one, not as "12.0".
        ScenePropKind.string => switch (value) {
          // A nullable string property — a font family — takes "nothing
          // set".
          null => null,
          double d when d == d.roundToDouble() && d.abs() < 1e15 =>
            '${d.round()}',
          _ => '$value',
        },
        ScenePropKind.number => (value! as num).toDouble(),
        ScenePropKind.edges => prop.quad!(value),
        ScenePropKind.size => sizeFromWire(value),
        _ => value,
      });
    case ScenePartKey(:var prop, :var part)
        when prop.kind == ScenePropKind.args:
      // Null is "not written": the argument leaves the map, so the file
      // spells nothing and the widget's own fallback answers.
      switch (node) {
        case SceneRefNode r:
          value == null ? r.args.remove(part) : r.args[part] = value;
        case ExternalNode e:
          value == null ? e.args.remove(part) : e.args[part] = value;
        default:
          break;
      }
    case ScenePartKey(:var prop, :var part)
        when prop.kind == ScenePropKind.axes:
      var axes = Map<String, double>.of(
        prop.read(node)! as Map<String, double>,
      );
      // Null puts the axis back at whatever the face itself defaults to,
      // which is what an absent tag means.
      value == null
          ? axes.remove(part)
          : axes[part] = (value as num).toDouble();
      prop.write(node, axes);
    case ScenePartKey(:var prop, :var sideIndex):
      // One side moves, the other three stay: a side is a number on a value
      // that is four of them.
      if (value == null) return;
      var current = prop.read(node)! as SceneQuad;
      prop.write(node, current.withSide(sideIndex, (value as num).toDouble()));
    case null:
      break;
  }
}