Start the panel slide after the window is on screen - #24
Merged
Conversation
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
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>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
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
showPanelandhidePanelanimate the same view the same way.Cause
#22 moved the reveal from animating the window to sliding the content inside a fixed panel frame.
showPanelstill started that animation in the same turn asorderFrontRegardless(). 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:
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.
hidePanelis unaffected because its window is already on screen when it animates.Fix
displayIfNeeded()and aCATransaction.flush()right after ordering the window in.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: