Skip to content

Pad the masthead against window controls on both edges - #33

Merged
perminder-klair merged 1 commit into
mainfrom
fix-windows-caption-button-flicker
Aug 12, 2026
Merged

Pad the masthead against window controls on both edges#33
perminder-klair merged 1 commit into
mainfrom
fix-windows-caption-button-flicker

Conversation

@perminder-klair

Copy link
Copy Markdown
Collaborator

A user on Windows 11 25H2 reported the icons at the top right flickering. Those are the OS min/max/close buttons, and the cause is on our side.

What was wrong

The main window is titlebar = "hidden_inset_tall", so the OS window controls overlay our own masthead. The SDK tells us where they land via WindowChrome.insets, and the cluster sits on a different edge per platform: macOS puts traffic lights on the left, Windows puts min/max/close on the right.

onChrome only mapped insets.top and insets.left. That is complete on macOS. On Windows insets.right is the full width of the caption cluster, roughly 140px, and we threw it away, so the update dot and the gear button were laid out underneath the DWM caption buttons.

That is also what makes them flicker. The Windows host runs syncHiddenCaptionColor on every present: it samples the app pixel 8px to the left of the cluster and pushes it through DWMWA_CAPTION_COLOR plus DWMWA_USE_IMMERSIVE_DARK_MODE, the latter deciding whether the button glyphs draw light or dark by luminance. With the gear button sitting exactly on that sample point, it read an antialiased glyph edge instead of flat header. Any wobble flips the colour, the flip forces a DWM caption repaint, and the light/dark verdict flips with it, so the glyphs strobe.

The SDK's own doc comment on WindowChrome states the contract we were breaking: "A header that pads BOTH insets.left and insets.right therefore clears the controls on every platform with no per-platform code."

The fix

ChromeInsets gains trailing, the model gains chrome_trailing, onChrome maps insets.right, and player-top.native ends with a <spacer width="{chrome_trailing}"/>. The row's existing gap="12" then keeps the sampled pixel on flat header rather than the gear's edge.

onboarding.native and lock.native already end in a growing spacer, so they were never affected and are untouched.

Testing

Test written first, failing on the missing field, passing after. 107/107 green, native check clean. A headless Linux screenshot is pixel identical, since chrome_trailing is honestly zero there.

Not verified on Windows. I have no Windows machine here. The layout bug is proven from the source: the host reports a non-zero right inset and we ignored it. The flicker mechanism is traced through the host rather than observed, though it is the only path I found that repaints those buttons. Worth confirming on a Windows build before we tell the reporter it is fixed.

The hidden_inset_tall masthead only padded WindowChrome.insets.left, so it
cleared the macOS traffic lights and nothing else. Windows puts its
min/max/close cluster on the right instead and reports it as insets.right,
which onChrome dropped on the floor. That left the update dot and the gear
button laid out underneath the DWM caption buttons.

It also made those buttons flicker. The Windows host resamples the app pixel
8px leading of the cluster on every present and pushes it through
DWMWA_CAPTION_COLOR plus DWMWA_USE_IMMERSIVE_DARK_MODE, which picks the button
glyph palette by luminance. With the gear sitting there the sample landed on an
antialiased glyph edge rather than flat header, so it flipped between readings,
each flip forced a caption repaint, and the light/dark verdict flipped with it.

Map both horizontal insets into chrome_leading / chrome_trailing and spacer
both ends of the row, which is the shape the SDK documents on WindowChrome.
The row's existing gap keeps the sampled pixel on flat header. onboarding and
lock already end in a growing spacer, so they were never affected, and Linux
is unchanged because the inset is honestly zero there.
@perminder-klair
perminder-klair merged commit 783aa19 into main Aug 12, 2026
4 checks passed
@perminder-klair
perminder-klair deleted the fix-windows-caption-button-flicker branch August 12, 2026 14:51
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.

1 participant