resize method

ShadResizeResult resize({
  1. required double delta,
  2. required List<double> initialLayout,
  3. required List<int> pivotIndices,
})

Adjusts the layout using an absolute percentage delta from the initial layout snapshot (captured at drag start).

This mirrors the react-resizable-panels approach: rather than applying incremental deltas, the entire layout is recalculated from the snapshot on every pointer move event. This eliminates floating-point drift and produces natural drag reversal behavior.

delta is the total drag distance as a fraction of the total available width (positive = forward/right/down, negative = backward/left/up).

initialLayout is the layout snapshot taken at drag start.

pivotIndices is the pair of panel indices adjacent to the dragged divider: [leadingIndex, trailingIndex].

Implementation

ShadResizeResult resize({
  required double delta,
  required List<double> initialLayout,
  required List<int> pivotIndices,
}) {
  if (delta.abs() < 1e-9) {
    _applyLayout(initialLayout);
    return ShadResizeResult.success;
  }

  final leadingPivot = pivotIndices[0];
  final trailingPivot = pivotIndices[1];
  final nextLayout = List<double>.from(initialLayout);
  var deltaApplied = 0.0;

  final originalDelta = delta;

  if (delta > 0) {
    // Forward drag: shrink trailing panels, grow leading pivot panel.

    // Phase 0: cap delta to min(totalShrinkable, totalExpandable).
    var totalShrinkable = 0.0;
    for (var i = trailingPivot; i < panelsInfo.length; i++) {
      totalShrinkable += initialLayout[i] - panelsInfo[i].minSize;
    }
    var totalExpandable =
        panelsInfo[leadingPivot].maxSize - initialLayout[leadingPivot];
    for (var i = leadingPivot - 1; i >= 0; i--) {
      totalExpandable += panelsInfo[i].maxSize - initialLayout[i];
    }
    final maxDelta = totalShrinkable < totalExpandable
        ? totalShrinkable
        : totalExpandable;
    final cappedDelta = delta > maxDelta ? maxDelta : delta;

    // Phase 1: shrink trailing panels from pivot outward.
    for (var i = trailingPivot; i < panelsInfo.length; i++) {
      final info = panelsInfo[i];
      final available = nextLayout[i] - info.minSize;
      if (available < 1e-9) continue;
      final shrink = (cappedDelta - deltaApplied).clamp(0.0, available);
      nextLayout[i] = (nextLayout[i] - shrink).asFixed(6);
      deltaApplied = (deltaApplied + shrink).asFixed(6);
      if ((cappedDelta - deltaApplied).abs() < 1e-9) break;
    }

    // Phase 2: grow leading pivot panel.
    final pivotInfo = panelsInfo[leadingPivot];
    final canGrow = pivotInfo.maxSize - nextLayout[leadingPivot];
    final grow = deltaApplied.clamp(0.0, canGrow);
    nextLayout[leadingPivot] = (nextLayout[leadingPivot] + grow).asFixed(6);
    var overflow = (deltaApplied - grow).asFixed(6);

    // Phase 3: cascade overflow to other leading panels.
    if (overflow > 1e-9) {
      for (var i = leadingPivot - 1; i >= 0; i--) {
        final info = panelsInfo[i];
        final canGrowI = info.maxSize - nextLayout[i];
        final growI = overflow.clamp(0.0, canGrowI);
        nextLayout[i] = (nextLayout[i] + growI).asFixed(6);
        overflow = (overflow - growI).asFixed(6);
        if (overflow < 1e-9) break;
      }
    }
  } else {
    // Backward drag: shrink leading panels, grow trailing pivot panel.
    final absDelta = -delta;
    var absApplied = 0.0;

    // Phase 0: cap delta to min(totalShrinkable, totalExpandable).
    var totalShrinkable = 0.0;
    for (var i = leadingPivot; i >= 0; i--) {
      totalShrinkable += initialLayout[i] - panelsInfo[i].minSize;
    }
    var totalExpandable =
        panelsInfo[trailingPivot].maxSize - initialLayout[trailingPivot];
    for (var i = trailingPivot + 1; i < panelsInfo.length; i++) {
      totalExpandable += panelsInfo[i].maxSize - initialLayout[i];
    }
    final maxDelta = totalShrinkable < totalExpandable
        ? totalShrinkable
        : totalExpandable;
    final cappedDelta = absDelta > maxDelta ? maxDelta : absDelta;

    // Phase 1: shrink leading panels from pivot outward.
    for (var i = leadingPivot; i >= 0; i--) {
      final info = panelsInfo[i];
      final available = nextLayout[i] - info.minSize;
      if (available < 1e-9) continue;
      final shrink = (cappedDelta - absApplied).clamp(0.0, available);
      nextLayout[i] = (nextLayout[i] - shrink).asFixed(6);
      absApplied = (absApplied + shrink).asFixed(6);
      if ((cappedDelta - absApplied).abs() < 1e-9) break;
    }

    deltaApplied = absApplied;

    // Phase 2: grow trailing pivot panel.
    final pivotInfo = panelsInfo[trailingPivot];
    final canGrow = pivotInfo.maxSize - nextLayout[trailingPivot];
    final grow = deltaApplied.clamp(0.0, canGrow);
    nextLayout[trailingPivot] = (nextLayout[trailingPivot] + grow).asFixed(6);
    var overflow = (deltaApplied - grow).asFixed(6);

    // Phase 3: cascade overflow to other trailing panels.
    if (overflow > 1e-9) {
      for (var i = trailingPivot + 1; i < panelsInfo.length; i++) {
        final info = panelsInfo[i];
        final canGrowI = info.maxSize - nextLayout[i];
        final growI = overflow.clamp(0.0, canGrowI);
        nextLayout[i] = (nextLayout[i] + growI).asFixed(6);
        overflow = (overflow - growI).asFixed(6);
        if (overflow < 1e-9) break;
      }
    }
  }

  // Validation: layout must sum to ~1.0.
  final sum = nextLayout.fold<double>(0, (a, b) => a + b);
  if ((sum - 1.0).abs() > 0.01) {
    // Layout is invalid; keep the current layout unchanged.
    return delta > 0
        ? ShadResizeResult.failedTrailing
        : ShadResizeResult.failedLeading;
  }

  _applyLayout(nextLayout);

  // Determine result based on whether the full original delta was consumed.
  if (originalDelta > 0) {
    // Total shrinkage on trailing side = total growth on leading side.
    var totalGrowth = 0.0;
    for (var i = 0; i <= leadingPivot; i++) {
      totalGrowth += nextLayout[i] - initialLayout[i];
    }
    if ((totalGrowth - originalDelta).abs() > 1e-6) {
      return ShadResizeResult.failedTrailing;
    }
  } else {
    var totalGrowth = 0.0;
    for (var i = trailingPivot; i < nextLayout.length; i++) {
      totalGrowth += nextLayout[i] - initialLayout[i];
    }
    if ((totalGrowth - (-originalDelta)).abs() > 1e-6) {
      return ShadResizeResult.failedLeading;
    }
  }

  return ShadResizeResult.success;
}