You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fit a new snap into the space beside a recently snapped window - #1903
Adds the off-by-default “Fit remaining space” option under Adjacent Windows. If a window has just been snapped to one side, the next complementary half-screen snap can use its actual edge instead of assuming a 50/50 split. Only the incoming window is resized; the existing neighbor stays in place.
Behavior and boundaries
Works for left/right and top/bottom half-screen pairs, respecting configured gaps and outer padding.
Uses a verified snap from the preceding 10 seconds; a displayed drag preview keeps that verified anchor until release, with geometry and identity rechecked. The neighbor must still belong to the same process instance, be visible, and remain at its recorded geometry. This does not infer a layout from any old window occupying one side of the screen.
Explicit fractions and repeated-command size cycling keep their requested geometry. If there is no valid recent complementary snap, normal placement applies.
Partial occlusion still allows the neighbor to participate. Complete coverage, including coverage by several visible windows together, does not. The incoming window is excluded from that occlusion check.
The remaining size is requested even if the application reports a larger minimum. An application may enforce a larger minimum and overlap its neighbor; Fit does not shrink or move that neighbor to compensate. A region with no positive extent is rejected.
Responsive grid-rounded and maximum-size results can settle smaller than requested and keep the configured edge alignment. Response evidence survives probes of the other axis. Bounded probes distinguish minimum/maximum constraints; one fixed axis can settle after the other axis responds, while wholly ignored resizing still fails.
Drag snaps preserve the pre-drag restore frame. Interrupted placements preserve restore history and retire stale action cycling.
Implementation
SnappedWindowFit resolves the target; a short-lived session owns the recent anchor. A shared serial placement coordinator verifies writes and handles cancellation without depending on animation interpolation. Helper and divider changes reuse placement utilities through their own feature-specific paths. Elements without an AX window ID retain the ordinary mover fallback using the resolved bookkeeping ID.
Rebuilt on current main without #1902. Placement uses reported AX constraints and verification from the current attempt; it does not read, learn, or persist window size limits. The proposed preview-only tracking feature is outside this change.
Validation
The current branch passed a signed Debug build and all 602 XCTest tests. Regression coverage includes rounded-down resizing, mixed minimum/grid constraints, an axis already at its maximum, fixed-axis settlement, wholly ignored resizing, and preview expiry. Native acceptance on macOS 27.2 / Apple silicon covers both axes and animation modes, both connected displays, disabled behavior and explicit fractions, gaps, anchor invalidation/occlusion, changing minima, drag restore history, and interruption by a subsequent shortcut. The 13-method native suite also checks the missing-AX-ID fallback and hidden-neighbor rejection. Its non-animated history test models a late write with a fixture frame change between cancellation and the next command. The occlusion fixture waits for WindowServer to confirm stacking order before executing the snap. The installed Rectangle binary and saved preferences are restored and verified after each run.
These are automated native acceptance results; they do not claim Intel or older-macOS execution. The demo below predates the standalone rebase.
An earlier physical-input acceptance run used global macOS mouse events to drag the incoming window to the complementary screen edge. It filled the remaining 576 points beside an 864-point anchor, with matching AX/WindowServer geometry and the anchor unchanged. The test used deterministic temporary snap settings; the installed binary, saved preferences, and pointer position were restored afterward.
There's a flaw in my design of WindowManager, where it becomes a place that needs to be modified in most changes to Rectangle functionality. Ideally, this change wouldn't have to modify the WindowManager, but would leverage a hook instead (which also goes for ShortcutManager). I'm debating whether this work should be done prior to merging this change. The different architecture would look a little more like a pipeline with hooks at each of the different stages of before/after calculations, adjusting calculations, and before/after applying the window placement. On this note, placeBesideSnappedWindow should be moved outside of WindowManager, and I think it would make more sense to have things like this live in files that hook into the pipeline as opposed to being directly inserted as part of it. I realize that these PRs have been in limbo for a while, but want to make sure that it's easy to build something stable. I'll give it a little more thought and post back when I decide on a direction.
There's a flaw in my design of WindowManager, where it becomes a place that needs to be modified in most changes to Rectangle functionality. Ideally, this change wouldn't have to modify the WindowManager, but would leverage a hook instead (which also goes for ShortcutManager). I'm debating whether this work should be done prior to merging this change. The different architecture would look a little more like a pipeline with hooks at each of the different stages of before/after calculations, adjusting calculations, and before/after applying the window placement. On this note, placeBesideSnappedWindow should be moved outside of WindowManager, and I think it would make more sense to have things like this live in files that hook into the pipeline as opposed to being directly inserted as part of it. I realize that these PRs have been in limbo for a while, but want to make sure that it's easy to build something stable. I'll give it a little more thought and post back when I decide on a direction.
That makes sense to me. Let me know how I could help with the refactoring!
Thanks for your understanding! I'm getting ready to wrap up some other app work and will rearchitect the WindowManager soon.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the off-by-default “Fit remaining space” option under Adjacent Windows. If a window has just been snapped to one side, the next complementary half-screen snap can use its actual edge instead of assuming a 50/50 split. Only the incoming window is resized; the existing neighbor stays in place.
Behavior and boundaries
Works for left/right and top/bottom half-screen pairs, respecting configured gaps and outer padding.
Uses a verified snap from the preceding 10 seconds; a displayed drag preview keeps that verified anchor until release, with geometry and identity rechecked. The neighbor must still belong to the same process instance, be visible, and remain at its recorded geometry. This does not infer a layout from any old window occupying one side of the screen.
Explicit fractions and repeated-command size cycling keep their requested geometry. If there is no valid recent complementary snap, normal placement applies.
Partial occlusion still allows the neighbor to participate. Complete coverage, including coverage by several visible windows together, does not. The incoming window is excluded from that occlusion check.
The remaining size is requested even if the application reports a larger minimum. An application may enforce a larger minimum and overlap its neighbor; Fit does not shrink or move that neighbor to compensate. A region with no positive extent is rejected.
Responsive grid-rounded and maximum-size results can settle smaller than requested and keep the configured edge alignment. Response evidence survives probes of the other axis. Bounded probes distinguish minimum/maximum constraints; one fixed axis can settle after the other axis responds, while wholly ignored resizing still fails.
Drag snaps preserve the pre-drag restore frame. Interrupted placements preserve restore history and retire stale action cycling.
Implementation
SnappedWindowFitresolves the target; a short-lived session owns the recent anchor. A shared serial placement coordinator verifies writes and handles cancellation without depending on animation interpolation. Helper and divider changes reuse placement utilities through their own feature-specific paths. Elements without an AX window ID retain the ordinary mover fallback using the resolved bookkeeping ID.Rebuilt on current
mainwithout #1902. Placement uses reported AX constraints and verification from the current attempt; it does not read, learn, or persist window size limits. The proposed preview-only tracking feature is outside this change.Validation
The current branch passed a signed Debug build and all 602 XCTest tests. Regression coverage includes rounded-down resizing, mixed minimum/grid constraints, an axis already at its maximum, fixed-axis settlement, wholly ignored resizing, and preview expiry. Native acceptance on macOS 27.2 / Apple silicon covers both axes and animation modes, both connected displays, disabled behavior and explicit fractions, gaps, anchor invalidation/occlusion, changing minima, drag restore history, and interruption by a subsequent shortcut. The 13-method native suite also checks the missing-AX-ID fallback and hidden-neighbor rejection. Its non-animated history test models a late write with a fixture frame change between cancellation and the next command. The occlusion fixture waits for WindowServer to confirm stacking order before executing the snap. The installed Rectangle binary and saved preferences are restored and verified after each run.
These are automated native acceptance results; they do not claim Intel or older-macOS execution. The demo below predates the standalone rebase.
An earlier physical-input acceptance run used global macOS mouse events to drag the incoming window to the complementary screen edge. It filled the remaining 576 points beside an 864-point anchor, with matching AX/WindowServer geometry and the anchor unchanged. The test used deterministic temporary snap settings; the installed binary, saved preferences, and pointer position were restored afterward.
This replaces the corresponding part of #1887.
Demo
06-fit-remaining-space.mp4
Window animation is disabled. Recorded with the combined build and only this feature and its required visual options enabled.
Settings