Skip to content

Add optional snapping animations and a blurred snap preview - #1849

Merged
rxhanson merged 27 commits into
rxhanson:mainfrom
kiteretsu903:dev/animation
Sep 16, 2026
Merged

rxhanson merged 27 commits into
rxhanson:mainfrom
kiteretsu903:dev/animation

Conversation

@kiteretsu903

Copy link
Copy Markdown
Contributor

This adds two options in Snap Areas, both off by default: Animate windows (experimental) and Blur footprint.

Snapping and restoring use smooth transitions, and the existing footprint animation follows the same easing curve. The blurred preview adapts to light and dark mode, with a subtle outline and outer shadow. Its tint uses the existing color and alpha preferences, defaulting to zero.

Animation remains experimental because repeatedly updating size and position may cause issues in some edge cases. Animations respect Reduce Motion, and blur respects Reduce Transparency.

This also balances the checkbox layout and updates the permission wording for macOS 27.

Comment thread Rectangle/Defaults.swift Outdated
}
}

class FootprintAlphaDefault: Default {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Rather than making a new default subclass, you'll want to use the DoubleDefault class, which allows an explicit zero.

@rxhanson

rxhanson commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Thanks! This is great :)

Looks like a merge conflict arose from another PR that I merged. Let me know if you need help resolving it.

Also, if you don't mind, can you clean up some of the comments that are added in here, as it looks like some of them are more for train of thought understanding than necessary communication in the codebase?

@rxhanson

rxhanson commented Sep 7, 2026

Copy link
Copy Markdown
Owner

On this note, I've given this a lot of thought prior to the PR, and one of the ideas I had for mitigating the limitations from the Accessibility API is to overlay a blurred window owned by Rectangle, move the actual window off the screen via Accessibility, resize the blurred window with native animation, then place the window at its destination as the animation concludes. This has its own set of usability issues, and doesn't work great for windows that don't fit the destination rect, but it might be worth exploring. Figured I'd mention it if you feel inclined to explore that route as well - no worries if not.

@kiteretsu903

kiteretsu903 commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor Author

@rxhanson Hi, Ryan, Thanks for quick response@
(1) fixed using DoubleDefault
(2) conflict resolved. Also improved the look of the "minimum window size reached" warning.
(3) improved comments
(4) For animation, I think your idea makes a lot of sense. I'll actually explore in that direction because I'm seeing some jumps on edge cases in my implementation. Will let you know once I have an update.
Thanks again for the helpful feedback!

@rxhanson

rxhanson commented Sep 9, 2026

Copy link
Copy Markdown
Owner

One edge case that's not working right is when Repeated commands is set to cycle sizes on side actions, then cycling on the right and bottom (like right half or bottom half) doesn't work. Even with the experimental disclaimer, this is something that would be quite nice to have working properly since it's really common to have that Repeated commands setting set. I don't mind taking a look when I get time if you don't get to it right away.

@kiteretsu903

Copy link
Copy Markdown
Contributor Author

One edge case that's not working right is when Repeated commands is set to cycle sizes on side actions, then cycling on the right and bottom (like right half or bottom half) doesn't work. Even with the experimental disclaimer, this is something that would be quite nice to have working properly since it's really common to have that Repeated commands setting set. I don't mind taking a look when I get time if you don't get to it right away.

Yep, will look into this!

@kiteretsu903
kiteretsu903 force-pushed the dev/animation branch 2 times, most recently from 0a7e457 to a59cea0 Compare September 13, 2026 03:08
@kiteretsu903

Copy link
Copy Markdown
Contributor Author

@rxhanson Hi Ryan, the right/bottom size cycling issue is fixed. I also tried your blurred-window approach, so there are now two options: Direct Resize (default) and a blur preview.

I’ve tested both across AppKit and browser windows comprehensively, including repeated commands, restore, size constraints, cross-display moves, and cancellation. Some edge cases like fast cross-display preview dragging still has some lag, so it remains experimental.

Thanks for reviewing it!

@kiteretsu903

Copy link
Copy Markdown
Contributor Author

@rxhanson This PR is finalized. Please take a look. Thanks!

@rxhanson

rxhanson commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

This is pretty awesome to see. Thanks for fixing the cycle resize issue and for implementing the blur preview approach. I'll give the updated code one more look through tomorrow, but the experience is good and don't think there are any additional feature changes that need to be made.

I might play around with the blur preview a little bit after merging, as I'm wondering if there might be some adjustments that could make it a little distracting. It would technically be possible to determine the window type based on where the stoplight buttons are, so as to match the corner radius and "copy" having the correct stoplight buttons present in a matching location. It also might be better to use a solid color window background instead of blur, but I'm not sure.

I'm excited to merge this change in.

Update: In using with the blur preview animation more, I think some acceleration would also help, on both moving the blur window and fading it out. Again, no pressure to make further changes, just some thoughts as I'm using it.

Also, looks like another merged PR caused a conflict, but I don't mind resolving it. I'll hold off on merging anything else until this one goes in.

Interpolate same-display resizing with a settling curve, preserve pending destinations for shortcut history, and let native dragging own position during size restoration. Respect Reduce Motion and keep existing mover fallbacks.
Use native blur with optional tint, a subtle outline, and an exterior shadow aligned at screen edges. Share the animation curve, respect reduced motion and transparency, and configure authored layers for SDR.
Add the two opt-in controls and their translations, and move haptic feedback into the left column.
Use Device Control and Data Access on macOS 27 and retain the earlier permission wording on older releases.
Only finish drag restoration on mouse-up when a window was actually being dragged. This prevents the drag monitor from prematurely settling a title-bar maximize or restore animation started by the same release event.

Document that title-bar double-click uses the existing snap animation setting. Release build passed; live event ordering has not been reproduced.
Detect movement from current displayed bounds, preserve the original grab reference, and let restoration finish after mouse release. Add regression coverage and document the remaining far-right drag jumps.
Add native drag handoff, preview rendering, verified parking and recovery, and constrained cross-display placement. Preserve animated title-bar and command restoration while applying drag restoration immediately.

Default new animation selections to Direct Resize, move Animate footprint to the left settings column, and expose the Blue preview label. Keep the existing repository tests compatible with the updated interfaces.

New animation tests, native harnesses, visual fixtures and historical evidence are maintained only in the local rectangle-animation-tests skill, outside the repository.

Rebase integration preserves upstream row/column tiling and alignment tolerance, shares its forceRefresh window-list API, and adapts its test fixture to position-aware frame writes.

Validation: 395 repository tests and 810 isolated local-skill tests pass; universal Release builds successfully.
Correct final alignment of constrained windows and retry native restore sizes that overshoot. Resize IINA once and align after its aspect correction, falling back when saved-size restoration is not achieved.

Validation: 844 tests, 33 final native layout cases, 16 repeated IINA transitions, and 6 installed-app checks. New tests and evidence remain in the local animation-test skill.
@kiteretsu903

Copy link
Copy Markdown
Contributor Author

@rxhanson Just resolved the conflicts. Let's keep this version for now, and I'll keep an eye on the further optimization like acceleration as well!

@rxhanson

Copy link
Copy Markdown
Owner

I've been continuing to test the blur preview animation out and hit a snag where windows that show the window size warning have a lot of lag with their positioning (thanks for making that warning look nicer, by the way).

The lag is painful enough, and it's a common enough scenario that I was thinking through a couple of options:

  1. Keep working on it
  2. Remove the option for the blur preview animation for now and solve the issue in a follow-on fix.

Since this PR is already pretty big, I'm leaning toward option 2, just so I can keep things moving forward, but am open to either path.

Still working my way through the code.

@kiteretsu903

Copy link
Copy Markdown
Contributor Author

@rxhanson Hey, Ryan. Agree that we can split blur preview into another branch, removing from this PR, while keeping other changes.

I can go ahead to do the split. Thanks for testing!
(btw i have been a rectangle user for a few years since intel mac and am very happy to contribute)

@kiteretsu903

Copy link
Copy Markdown
Contributor Author

@rxhanson
Further fixed some issues and added an independent animation curve path optimized for keyboard-triggered snapping. Thought we could finalize this one!
I also split the blur preview path into another PR #1859

@rxhanson

Copy link
Copy Markdown
Owner

Thanks! I removed the markdown and the main.swift entry point, and added back in the @main entry point to the AppDelegate. I'm going to go back through the WindowManager changes with fresh eyes tomorrow morning and will merge then as long as I don't have anything I want to change there 👍

@rxhanson
rxhanson merged commit 7e7714e into rxhanson:main Sep 16, 2026
anandghegde added a commit to anandghegde/Rectangle that referenced this pull request Sep 21, 2026
The test double overrides setFrame(_:adjustSizeFirst:), but
AccessibilityElement.setFrame gained an adjustPosition parameter in
"Add optional snapping animations and a blurred snap preview" (rxhanson#1849),
so the override no longer matches and the test target does not compile:

  RectangleTests.swift:6153:23: error: method does not override any
  method from its superclass

This is not related to the rest of the branch - it reproduces on a clean
checkout - but the test target has to build before any of the tests can
run.
rxhanson pushed a commit that referenced this pull request Sep 24, 2026
* Keep a window's position when moving it to another display

Moving a window to the next, previous or a specific display centered it
on the destination, which loses the spot the user had put it in.

DisplayTransfer works out each axis on its own from whether the window
was against a screen edge: against both edges it follows both and spans
the destination, against one it keeps its size and stays against that
edge, against neither it keeps its size and its center keeps the same
relative spot. The distance from an edge is carried over rather than
flattened, so a window snapped with gaps arrives with the same gaps.

This is now the default for next/previous and specific display moves.
The old centering is available as centerOnDisplayChange, and
attemptMatchOnNextPrevDisplay still replays the last Rectangle action
where there is one.

Closes #1666

* RectangleTests: match the current setFrame signature

The test double overrides setFrame(_:adjustSizeFirst:), but
AccessibilityElement.setFrame gained an adjustPosition parameter in
"Add optional snapping animations and a blurred snap preview" (#1849),
so the override no longer matches and the test target does not compile:

  RectangleTests.swift:6153:23: error: method does not override any
  method from its superclass

This is not related to the rest of the branch - it reproduces on a clean
checkout - but the test target has to build before any of the tests can
run.

* Center three-edge windows along the middle edge across orientations

A window against three screen edges - spanning one axis and against one
edge of the other, like a left half or a top half - was stretched along
the whole of the spanning axis when it moved between a landscape and a
portrait display, turning a left half into a tall sliver. It now keeps
its size and is centered along the middle one of its three edges.
Picking a corner instead would mean guessing from how the displays are
arranged; centering makes no assumption.

Keeping a window's position is now a setting, "Keep window position
when moving across displays" in the Behavior tab, backed by
keepWindowPositionOnDisplayChange. It is on by default. Turned off,
display moves behave as they did before this branch: the window is
centered, or with attemptMatchOnNextPrevDisplay on and no action to
replay, mapped proportionally as in #1809. relativePositionedRect and
its tests are restored for that path, and the centerOnDisplayChange
terminal command is gone, since the setting replaces it.
@kiteretsu903
kiteretsu903 deleted the dev/animation branch October 13, 2026 03:46
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