Skip to content

Add window edge snapping resistance#150

Merged
pablopunk merged 6 commits into
mainfrom
fractal/let-s-plan-tackle-https-github-com-pablopunk-668719
May 13, 2026
Merged

Add window edge snapping resistance#150
pablopunk merged 6 commits into
mainfrom
fractal/let-s-plan-tackle-https-github-com-pablopunk-668719

Conversation

@pablopunk
Copy link
Copy Markdown
Owner

@pablopunk pablopunk commented May 13, 2026

Summary

  • Add subtle 10px snapping resistance when moving/resizing near neighboring window edges
  • Cache visible neighboring window rects at tracking start for performance
  • Only snap against windows overlapping on the perpendicular axis
CleanShot.2026-05-13.at.18.50.29.yafw.balanced.mp4

Fixes #44

Test plan

  • Built successfully with xcodebuild -project "Swift Shift.xcodeproj" -scheme "Swift Shift" -configuration Debug build
  • Manually tested window snapping behavior while moving/resizing

Summary by CodeRabbit

  • New Features

    • Windows snap to nearby visible windows when moving or resizing for cleaner alignment.
    • Snapping uses smarter edge-based matching and filters out the active window itself from snap candidates.
  • Bug Fixes / Improvements

    • More responsive tracking with a longer timeout.
    • Prevents zero-size resize results and avoids redundant move/resize updates for greater stability.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
swiftshift-app Ready Ready Preview, Comment May 13, 2026 5:22pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR implements window snapping for move and resize operations. WindowManager.getVisibleWindowRects(excluding:) collects on-screen window rectangles. MouseTracker adds snapRects, snapDistance, and last-applied state, populates snapRects when tracking starts, and clears them on reset. Move handling replaces the computed origin with a snapped origin based on nearest X/Y edge deltas within snapDistance. Resize handling clamps dimensions to >=1, computes snapped origin/size using active edges (quadrant-driven fallback), and applies WindowManager.resize only when snapped values differ from the last applied ones.

Sequence Diagram(s)

sequenceDiagram
  participant MouseTracker
  participant WindowManager
  participant CGWindowList

  MouseTracker->>WindowManager: getVisibleWindowRects(excluding: currentAXWindow?)
  WindowManager->>CGWindowList: CGWindowListCopyWindowInfo(query on-screen windows)
  CGWindowList-->>WindowManager: window info list
  WindowManager-->>MouseTracker: [CGRect] visible window rects
  MouseTracker->>MouseTracker: compute target origin/size (move or resize)
  MouseTracker->>MouseTracker: compute snapped origin/size using snapRects & snapDistance
  MouseTracker->>WindowManager: move/resize window to snapped origin/size
  WindowManager-->>MouseTracker: confirmation (implicit)
Loading

Possibly related PRs

  • pablopunk/SwiftShift#109: Modifies MouseTracker move/resize control flow and last-applied update behavior, which relates to this PR's snapping application logic.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add window edge snapping resistance' accurately summarizes the main objective from issue #44 to provide resistance when window edges reach adjacent window edges.
Linked Issues check ✅ Passed The changes implement the core requirement from issue #44: adding snapping resistance when window edges approach or reach adjacent window edges during move/resize operations.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing window edge snapping resistance: MouseTracker handles snap logic, WindowManager provides visible window rectangles, and there are no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Swift` Shift/src/Manager/WindowManager.swift:
- Around line 23-41: getVisibleWindowRects currently iterates apps'
kAXWindowsAttribute (AXUIElement) causing inclusion of minimized/off‑space
windows and synchronous AX round‑trips during prepareTracking; replace the AX
round‑trip with reading kCGWindowBounds from each CGWindowListCopyWindowInfo
entry (which was already filtered with .optionOnScreenOnly) similar to
getTopWindowAtCursorUsingCGWindowList, building rects directly from
kCGWindowBounds to avoid AX queries and eliminate invisible/off‑space windows;
if you must keep AX, add checks for kAXMinimizedAttribute and intersect each
rect with NSScreen.screens frames to exclude off‑space windows and minimize AX
calls during prepareTracking.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2d621e4c-f2ca-4cd6-902e-b12f1e329169

📥 Commits

Reviewing files that changed from the base of the PR and between e92aaf4 and 73f2596.

📒 Files selected for processing (2)
  • Swift Shift/src/Manager/MouseTracker.swift
  • Swift Shift/src/Manager/WindowManager.swift

Comment thread Swift Shift/src/Manager/WindowManager.swift
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Swift` Shift/src/Manager/WindowManager.swift:
- Line 37: The exclusion check in WindowManager.swift incorrectly filters any
window that fully contains the dragged window by using
rect.intersection(excludedRect).equalTo(excludedRect); change the logic so it
only excludes the exact same window by comparing the rects for exact equality
(use rect.equalTo(excludedRect) when unwrapping excludedRect) instead of using
intersection(); update the conditional around excludedRect and rect to return
nil only when the two rects are exactly equal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7df5245-bde2-4e96-bcb5-0efe581bfea8

📥 Commits

Reviewing files that changed from the base of the PR and between 73f2596 and aa643c0.

📒 Files selected for processing (1)
  • Swift Shift/src/Manager/WindowManager.swift

Comment thread Swift Shift/src/Manager/WindowManager.swift Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Swift Shift/src/Manager/WindowManager.swift (1)

37-37: 🏗️ Heavy lift

Consider using a more robust method to identify and exclude the dragged window.

The excludedRect is computed from the AX API (getPosition/getSize) while rect comes from kCGWindowBounds in CGWindowListCopyWindowInfo. Comparing them with exact floating-point equality via equalTo is fragile and may fail if the two APIs produce slightly different values due to precision differences or timing. While no issues are currently reported, this could cause the dragged window to appear in snap targets under edge cases.

Prefer using the window's CGWindowID for comparison: extract it from info[kCGWindowNumber as String] as? CGWindowID and derive a matching ID from the AX element via _AXUIElementGetWindow. Alternatively, add a small epsilon tolerance to the rect comparison if window IDs are unavailable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Swift` Shift/src/Manager/WindowManager.swift at line 37, The current exact
rect comparison (rect.equalTo(excludedRect)) is fragile; instead use the
window's CGWindowID for robust exclusion: extract the CGWindowID from the
CGWindowList info via info[kCGWindowNumber as String] as? CGWindowID and obtain
the AX element's window ID via _AXUIElementGetWindow, then compare IDs to skip
the dragged window (fallback: if IDs unavailable, use rect comparison with a
small epsilon/tolerance). Update the logic around excludedRect/rect in
WindowManager.swift to prefer ID comparison, falling back to tolerant rect
comparison.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Swift` Shift/src/Manager/WindowManager.swift:
- Line 36: The current check uses NSRunningApplication(processIdentifier:
pid)?.bundleIdentifier and only filters when a bundleId exists, thereby
including windows with nil bundle identifiers; change this to explicitly exclude
nil bundle identifiers by making the conditional require a non-nil bundleId
(e.g., replace the optional binding with a guard/if-let that returns nil when
bundleId is nil) or, if inclusion is desired, add a PreferencesManager flag
(e.g., PreferencesManager.includeAppsWithoutBundleId) and use that flag
alongside NSRunningApplication(processIdentifier: pid)?.bundleIdentifier and
PreferencesManager.isAppIgnored(bundleId) to decide whether to return nil;
update the logic around NSRunningApplication(processIdentifier:
pid)?.bundleIdentifier and PreferencesManager.isAppIgnored to reflect the chosen
behavior.

---

Nitpick comments:
In `@Swift` Shift/src/Manager/WindowManager.swift:
- Line 37: The current exact rect comparison (rect.equalTo(excludedRect)) is
fragile; instead use the window's CGWindowID for robust exclusion: extract the
CGWindowID from the CGWindowList info via info[kCGWindowNumber as String] as?
CGWindowID and obtain the AX element's window ID via _AXUIElementGetWindow, then
compare IDs to skip the dragged window (fallback: if IDs unavailable, use rect
comparison with a small epsilon/tolerance). Update the logic around
excludedRect/rect in WindowManager.swift to prefer ID comparison, falling back
to tolerant rect comparison.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b57ab647-45d1-4769-aa66-9f963f653d68

📥 Commits

Reviewing files that changed from the base of the PR and between 3a944bc and 0679dda.

📒 Files selected for processing (1)
  • Swift Shift/src/Manager/WindowManager.swift

Comment thread Swift Shift/src/Manager/WindowManager.swift Outdated
@pablopunk pablopunk merged commit 05fcefe into main May 13, 2026
6 checks passed
@pablopunk pablopunk deleted the fractal/let-s-plan-tackle-https-github-com-pablopunk-668719 branch May 13, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add resistance when reaching the edge of another window

1 participant