mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 23:24:32 +00:00
159 lines
6.2 KiB
Markdown
159 lines
6.2 KiB
Markdown
# 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.
|
|
|
|
## Recommended Approach
|
|
|
|
**Option 2** seems safest: Remove dynamic window resizing entirely and let the content handle sizing.
|
|
|
|
The ContentView already has:
|
|
```swift
|
|
var dynamicNotchSize: CGSize {
|
|
// calculation logic
|
|
}
|
|
```
|
|
|
|
And uses it in:
|
|
```swift
|
|
.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.
|
|
|
|
## CRITICAL DISCOVERY - Clipboard is NOT a Tab View
|
|
|
|
### Major Finding
|
|
**The `.clipboard` case doesn't exist in `NotchViews` enum!**
|
|
|
|
```swift
|
|
public enum NotchViews {
|
|
case home
|
|
case shelf
|
|
case timer
|
|
case stats
|
|
}
|
|
```
|
|
|
|
### What This Means
|
|
- All my protective logic for `coordinator.currentView == .clipboard` is **ineffective**
|
|
- Clipboard functionality works differently - likely as a popover or overlay, not a tab
|
|
- The hover collapse issue is NOT about tab transitions but about popover interactions
|
|
|
|
### Investigation Results
|
|
1. **ContentView switch statement** only handles: `.home`, `.shelf`, `.timer`, `.stats`
|
|
2. **No clipboard tab content rendering** in the main view switch
|
|
3. **Clipboard must be implemented as popover/overlay** system
|
|
|
|
### New Hypothesis
|
|
The clipboard "area" the user refers to is likely:
|
|
1. **ClipboardHistoryPopover** - a popover that opens over the Dynamic Island
|
|
2. **ClipboardManager integration** - clipboard content shown in existing tabs
|
|
3. **Overlay system** - clipboard UI overlaid on top of main content
|
|
|
|
### Critical Issue Identified
|
|
My previous fix added protections for `.clipboard` view that **DOESN'T EXIST**, meaning:
|
|
- Protections are not working
|
|
- Hover collapse still happening during clipboard interactions
|
|
- Need to find actual clipboard interaction boundaries
|
|
|
|
### Next Steps
|
|
1. **Find real clipboard implementation** - how is clipboard content actually shown?
|
|
2. **Identify clipboard hover boundaries** - what UI element represents "clipboard area"?
|
|
3. **Fix actual interaction conflicts** - coordinate between clipboard popover and main hover logic
|
|
|
|
#### 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:
|
|
```swift
|
|
// OLD - ALWAYS used openNotchSize
|
|
let window = DynamicIslandWindow(
|
|
contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width, height: openNotchSize.height),
|
|
...
|
|
```
|
|
|
|
#### The Fix:
|
|
```swift
|
|
// 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!
|