resolveDragArena function

ActiveGesture? resolveDragArena({
  1. required Offset totalDelta,
  2. required Map<DragStart, DragGesture> registered,
  3. double minDistance = 10,
})

Resolves a drag gesture from the registered map for the accumulated motion vector. Returns the matching ActiveGesture, or null if no axis has crossed minDistance yet, or no registered key matches.

Resolution: per-axis threshold (each axis must independently cross minDistance to count). Candidates are tried in priority order from most-specific to least-specific:

  1. Diagonal (when both axes crossed)
  2. Dominant (axis ≥ 2× the other)
  3. Primary direction (the dominant axis's specific direction)
  4. Secondary direction (the other axis's specific direction)
  5. horizontal / vertical (axis-only)
  6. any

First registered match wins. No friction-based scoring — direction-key specificity is the contract.

Implementation

ActiveGesture? resolveDragArena({
  required Offset totalDelta,
  required Map<DragStart, DragGesture> registered,
  double minDistance = 10,
}) {
  final dx = totalDelta.dx;
  final dy = totalDelta.dy;
  final adx = dx.abs();
  final ady = dy.abs();
  if (adx < minDistance && ady < minDistance) return null;

  final hCrossed = adx >= minDistance;
  final vCrossed = ady >= minDistance;
  final DragStart? hDir = hCrossed ? (dx > 0 ? .right : .left) : null;
  final DragStart? vDir = vCrossed ? (dy > 0 ? .down : .up) : null;

  // Diagonal — both axes crossed.
  DragStart? diagonal;
  if (hCrossed && vCrossed) {
    diagonal = switch ((hDir, vDir)) {
      (.left, .up) => .upLeft,
      (.right, .up) => .upRight,
      (.left, .down) => .downLeft,
      (.right, .down) => .downRight,
      _ => null,
    };
  }

  // Dominant — one axis ≥ 2× the other.
  DragStart? dominant;
  if (hCrossed && adx >= 2 * ady) {
    dominant = dx > 0 ? .rightDominant : .leftDominant;
  } else if (vCrossed && ady >= 2 * adx) {
    dominant = dy > 0 ? .downDominant : .upDominant;
  }

  // Primary / secondary by dominant-axis preference.
  final hPrimary = !vCrossed || (hCrossed && adx >= ady);
  final primary = hPrimary ? hDir : vDir;
  final secondary = hPrimary ? vDir : hDir;

  for (final candidate in <DragStart?>[
    diagonal,
    dominant,
    primary,
    secondary,
    if (hCrossed) .horizontal,
    if (vCrossed) .vertical,
    .any,
  ]) {
    if (candidate == null) continue;
    final gesture = registered[candidate];
    if (gesture != null) return (start: candidate, gesture: gesture);
  }
  return null;
}