Skip to content

Start the panel slide after the window is on screen - #24

Merged
mobrava merged 1 commit into
mainfrom
fix/panel-open-animation-pop
Sep 2, 2026
Merged

Start the panel slide after the window is on screen#24
mobrava merged 1 commit into
mainfrom
fix/panel-open-animation-pop

Conversation

@evowhale

@evowhale evowhale commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Problem

Found while testing 1.3.1 before release: closing the panel slides down correctly, but opening it pops in. The motion is asymmetric even though showPanel and hidePanel animate the same view the same way.

Cause

#22 moved the reveal from animating the window to sliding the content inside a fixed panel frame. showPanel still started that animation in the same turn as orderFrontRegardless(). Ordering a window in is not instant, the window server needs a composited frame before anything reaches the screen.

Instrumented a release build and logged timestamps across six open/close cycles:

ordered-in -> anim-start :  62, 51, 48, 60, 47, 53 ms   (mean 53)
anim-start -> anim-end   : 278, 261, 257, 261, 259, 263 ms  (duration is 250)
y at anim-start          : -280.0 every time

The slide is 250ms easeOut. A 53ms head start is 20% of the duration and roughly 36% of the distance on that curve, so the panel became visible already a third of the way up. hidePanel is unaffected because its window is already on screen when it animates.

Fix

  • Push the parked first frame to the screen with displayIfNeeded() and a CATransaction.flush() right after ordering the window in.
  • Start the slide on the next main actor turn, so the whole easeOut curve happens while the panel is visible.
  • Drop the window shadow before orderFrontRegardless() rather than after, so the first composited frame never draws a shadow around empty space.

Cost

Content starts moving about 50ms later than before. Hotkey to fully open is about 310ms. Still far better than the 168ms of panel construction this path paid before #18.

Testing

Release build (Developer ID, notarized), installed to /Applications, single display:

The reveal animation ran on the content inside a fixed panel frame, but it
was started in the same turn as `orderFrontRegardless()`. Ordering a window
in is not instant: the window server needs a composited frame first.
Measured on a real launch, that gap is 47-62ms (six samples, mean 53ms).

The slide is a 250ms easeOut, so those ~50ms are 20% of the duration and,
on that curve, about 36% of the distance. The panel therefore materialised
already a third of the way up and read as a pop rather than a slide.
`hidePanel` was unaffected because its window is already on screen when it
animates, which is why closing looked right and opening did not.

Push the parked first frame out with `displayIfNeeded()` + a CATransaction
flush, then start the slide on the next main actor turn so the whole curve
happens on screen. Also drop the window shadow before ordering in, so the
first composited frame never carries a shadow around empty space.

Costs ~50ms before the content starts moving. Total hotkey-to-open is about
310ms, still well below the 168ms build cost this path had before #18.
@mobrava
mobrava merged commit 2294750 into main Sep 2, 2026
1 check passed
@mobrava
mobrava deleted the fix/panel-open-animation-pop branch September 2, 2026 14:32
mobrava added a commit that referenced this pull request Sep 2, 2026
The reveal animation ran on the content inside a fixed panel frame, but it
was started in the same turn as `orderFrontRegardless()`. Ordering a window
in is not instant: the window server needs a composited frame first.
Measured on a real launch, that gap is 47-62ms (six samples, mean 53ms).

The slide is a 250ms easeOut, so those ~50ms are 20% of the duration and,
on that curve, about 36% of the distance. The panel therefore materialised
already a third of the way up and read as a pop rather than a slide.
`hidePanel` was unaffected because its window is already on screen when it
animates, which is why closing looked right and opening did not.

Push the parked first frame out with `displayIfNeeded()` + a CATransaction
flush, then start the slide on the next main actor turn so the whole curve
happens on screen. Also drop the window shadow before ordering in, so the
first composited frame never carries a shadow around empty space.

Costs ~50ms before the content starts moving. Total hotkey-to-open is about
310ms, still well below the 168ms build cost this path had before #18.

Co-authored-by: mobrava <82764703+mobrava@users.noreply.github.com>
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