Skip to content

Anchor clamped windows to the snap edge instead of leaving a gap#1778

Merged
rxhanson merged 4 commits into
rxhanson:mainfrom
LoriTira:edge-anchor-clamped-windows
Jun 16, 2026
Merged

Anchor clamped windows to the snap edge instead of leaving a gap#1778
rxhanson merged 4 commits into
rxhanson:mainfrom
LoriTira:edge-anchor-clamped-windows

Conversation

@LoriTira

@LoriTira LoriTira commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

When a window can't be resized to fill its snap zone, Rectangle leaves it at the zone's leading corner, producing a gap on the screen-edge side. The clearest repro is FaceTime snapped to a half (when in a vertical video call): it keeps a fixed video aspect ratio, so on a wide display it clamps its own width and ends up floating near screen-center instead of flush against the screen edge. This also happens with the System Settings window for example.

This makes a clamped window anchor to the snap zone's screen edge(s), and stay centered on any axis it can't fill. It's all behind the existing moveFixedSizeToEdge default, which is off by default, so default behavior is unchanged.

Why it happens today

  • FaceTime is resizable (isResizable() is true), so it goes through the standard mover chain and never reaches FixedSizeWindowMover. StandardWindowMover sets the zone position, the app clamps the size, and BestEffortWindowMover only rescues windows that overflow off the screen — an under-filled window keeps its leading-corner position, leaving the gap.
  • The existing moveFixedSizeToEdge edge-anchoring in FixedSizeWindowMover was gated on sharedEdges.isCorner, so it only fired for quarter snaps, never halves (a half shares three screen edges, so isCorner is false).

Changes

  • ClampedWindowAligner — a pure, unit-tested helper that repositions a window per axis: if the zone touches exactly one screen edge on that axis, anchor to it; otherwise (spans the full axis, or floats inside) center. It subsumes the old matchSharedEdges / centerWindowRect and drops the isCorner gate.
  • FixedSizeWindowMover now delegates to it, so half-snaps of fixed-size windows anchor correctly too. (Flag off → .none shared edges → centers, exactly as before.)
  • EdgeAlignmentWindowMover added to the standard chain to re-anchor resizable-but-clamped windows like FaceTime after StandardWindowMover runs.
  • Documented the (now broader) moveFixedSizeToEdge flag in TerminalCommands.md.

Edge detection and anchoring use the gap-applied rect, so existing gap settings are respected.

Test plan

  • New unit tests for ClampedWindowAligner (right half, left half, full-height half, top-right quarter, interior, exact-fit): 6/6 pass.
  • Full app builds.
  • Manual, with defaults write com.knollsoft.Rectangle moveFixedSizeToEdge -bool true:
    • FaceTime snapped to the right/left half now sits flush against the screen edge, vertically centered (previously a gap).
    • Quarters snug into the correct corner.
    • Normal windows that fill a half, and all behavior with the flag off, are unchanged.

@rxhanson

Copy link
Copy Markdown
Owner

Thanks! This is great. There's one thing that I'm thinking about on it - that perhaps it would be nice to provide the granularity of configuration up front for whether or not this should apply to halves. We could change moveFixedSizeToEdge to an IntEnumDefault, where 0 is disabled, 1 is this behavior, and 2 is gated on corners. In my testing it was nice for some apps to be centered on left/right half. I'm ok merging this and rolling that in as well, if you're not interested in it.

@rxhanson

Copy link
Copy Markdown
Owner

I'll go ahead and merge this one in and adjust the moveFixedSizeToEdge with more granularity in a follow-on change. Thanks again!

@rxhanson rxhanson merged commit 1f6af7a into rxhanson:main Jun 16, 2026
1 check passed
@LoriTira

Copy link
Copy Markdown
Contributor Author

Great! And thank you for dealing with the moveFixedSizeToEdge adjusting later!

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.

2 participants