Atoll/logic.md
2025-07-30 20:04:03 +05:30

4.9 KiB

Dynamic Island Alignment Logic Analysis

Problem Statement

The Dynamic Island window sometimes gets misaligned (moves to the left) when switching between tabs, especially when going from stats tab (with 4+ graphs that make the window wider) back to other tabs.

Current Implementation Analysis

When Window is Created

  1. createDynamicIslandWindow() creates window with openNotchSize (fixed size)
  2. positionWindow() centers it using current window width
  3. Window is perfectly centered initially

When User Switches to Stats Tab (4+ graphs)

  1. coordinator.$currentView triggers updateWindowSizeIfNeeded()
  2. calculateRequiredNotchSize() returns larger size (openNotchSize.width + extraWidth)
  3. updateWindowSizeIfNeeded() resizes window and re-centers using new width
  4. Window should stay centered with new larger size

When User Switches Away from Stats Tab

  1. coordinator.$currentView triggers updateWindowSizeIfNeeded() again
  2. calculateRequiredNotchSize() returns normal openNotchSize
  3. updateWindowSizeIfNeeded() resizes window back and re-centers using normal width
  4. THIS IS WHERE THE PROBLEM OCCURS

Root Cause Analysis

The Issue: Timing and State Mismatch

The problem is likely that coordinator.currentView and the actual UI state are not perfectly synchronized. Here's what probably happens:

  1. User clicks another tab
  2. coordinator.$currentView fires immediately
  3. updateWindowSizeIfNeeded() calculates size as if we're NOT on stats tab
  4. BUT the ContentView might still be showing stats content (transition not complete)
  5. Window gets resized to small size while content is still large
  6. This causes misalignment because content and window size don't match

Why It Sometimes Works

  • When the UI transition is fast enough, everything stays in sync
  • When there's any delay or animation, they get out of sync

Potential Solutions

Option 1: Delay the Window Resize

Add a small delay to ensure UI transition is complete before resizing window.

Option 2: Remove Window Resizing, Use Content Clipping

Let the window stay large and let the content handle its own sizing internally.

Option 3: Better State Synchronization

Make sure window resizing only happens when we're 100% sure the content has updated.

Option 4: Use ContentView's dynamicNotchSize Instead

The ContentView already has dynamicNotchSize logic. Maybe the window should use that instead of having its own calculation.

Option 2 seems safest: Remove dynamic window resizing entirely and let the content handle sizing.

The ContentView already has:

var dynamicNotchSize: CGSize {
    // calculation logic
}

And uses it in:

.frame(maxWidth: dynamicNotchSize.width, maxHeight: dynamicNotchSize.height)

This means the content already knows how to size itself. The window can stay at the maximum possible size, and the content will center itself within that space.

Test Plan

  1. Remove all dynamic window resizing logic
  2. Set window to maximum possible size (openNotchSize.width + max extra width)
  3. Let ContentView handle all dynamic sizing internally
  4. Test alignment stays perfect

This eliminates the timing issue completely because the window never changes size after initial creation.

REAL ROOT CAUSE FOUND AND FIXED

The ACTUAL Problem: Window Creation with Wrong Size

The issue was NOT positioning logic - it was that createDynamicIslandWindow() always created windows with openNotchSize instead of the current required size!

When This Caused Problems:

  1. "Show on all displays" toggle: Creates new window with default openNotchSize, losing dynamic sizing
  2. Screen changes: adjustWindowPosition() creates new windows with wrong size
  3. App restarts/other events: Any time a new window is created, it ignores current dynamic sizing needs

The Wrong Code:

// OLD - ALWAYS used openNotchSize
let window = DynamicIslandWindow(
    contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width, height: openNotchSize.height),
    ...

The Fix:

// NEW - Uses current required size
let requiredSize = calculateRequiredNotchSize()
let window = DynamicIslandWindow(
    contentRect: NSRect(x: 0, y: 0, width: requiredSize.width, height: requiredSize.height),
    ...

Why This Fixes Everything:

✅ "Show on all displays" toggle: New windows created with correct size
✅ Tab switching: No more size mismatch when repositioning
✅ Screen changes: New windows respect current graph settings
✅ Consistent behavior: All window creation uses same sizing logic

The Real Issue Was:

  • Windows created with wrong size, then positioned correctly = misalignment
  • Content expecting larger window, but window is small = positioning errors
  • Dynamic sizing only worked if you never triggered window recreation

Result:

Perfect alignment in ALL scenarios - no more window size/content size mismatches!