Skip to content

Animate tiles from window frames#26

Merged
peterp merged 7 commits into
mainfrom
animate-from-window-frame-with-changeset
May 26, 2026
Merged

Animate tiles from window frames#26
peterp merged 7 commits into
mainfrom
animate-from-window-frame-with-changeset

Conversation

@peterp
Copy link
Copy Markdown
Owner

@peterp peterp commented May 26, 2026

Problem

The overlay animation felt less connected to the real windows. Tiles moved from the grid, but they did not clearly start and land on the window frames users already saw on screen.

There was also no way to tune the new motion. If the speed felt too slow or too fast, users had to accept the default.

Solution

Tiles now animate between their grid cells and each window's real screen frame. Picking a window also waits until the animation lands before activating the real window, which makes the handoff feel smoother.

This also adds an animationSpeed config value and a Settings slider. Users can slow the animation down or speed it up from 0.25× to 4×.

Supersedes #25 because that fork branch does not allow maintainer edits.

ericclemmons and others added 7 commits May 24, 2026 12:30
The show/pick animations previously started or ended at the overlay's
full bounds, so every tile flew in or out from the same big rectangle
regardless of where the underlying window actually sat. Convert each
tile's CGWindowList frame (top-left, global) into overlay content-view
coordinates and use that as the source/target frame, so tiles fly to
and from where their windows really live.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Show and dismiss now fly *all* tiles between their grid cells and the
real window frames they snapshot, not just the focused one. The backdrop
moves to its own CALayer so it can fade independently of the tiles, and
both fades run in parallel with the tile movement at the same duration.
Each tile's zPosition mirrors the WindowServer stacking order so
overlapping tiles maintain the same overlap as the live windows at the
start of show and end of dismiss. Easing switched to a symmetric
ease-in-out so the slide feels even on both ends.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calling raiseAXWindow and app.activate up front let WindowServer reorder
the real windows behind the dim backdrop before the tile animation even
started, which read as a flash. Move both calls into the dismiss
transaction's completion block, with a small pause on either side: one
so the slide reads as "done" before any real-window movement, and one to
let WindowServer apply the reorder before the overlay disappears.
setCompletionBlock fires after the actual last rendered frame instead of
guessing from a delay.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The system paints a drop shadow on the real window the moment
activation runs. Without one on the tile, you see the shadow pop in
outside the tile's edges as the overlay disappears. Animate a matching
shadow onto the picked tile during the dismiss so the handoff is
seamless.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Letter-prefix typing dims non-matching tiles to 0.3 via applyTileLabels.
When the final character resolved a unique match and triggered pick(),
those dimmed tiles flew back to their source frames still at 0.3, so the
animation looked ghostly compared to clicking. Snap every tile's opacity
back to 1 at the start of pick() so the dismiss looks identical in both
paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@peterp peterp mentioned this pull request May 26, 2026
@peterp peterp merged commit 34ed8ad into main May 26, 2026
1 check passed
@peterp peterp mentioned this pull request May 26, 2026
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