fix(maps): create the map window with an alpha channel so the backdrop works - #100
Merged
Merged
Conversation
…p works The backdrop opacity appeared to do nothing after the ghosting fix (#65), and then "seemed to start working after clicking Apply twice". The value was never late: tracing a real Apply shows _apply_maps writing the legacy key, then config_updated reaching Maps.sync_map_chrome, which calls apply_backdrop_opacity ONCE with the new value — brush, mode and a full viewport repaint all correct on the first click. What an Apply actually did was reach ParserWindow.apply_window_state -> _set_flags -> setWindowFlags, which RECREATES the native window. That recreation was the fix, because the window had no alpha channel to composite into. Maps._build_chrome set WA_TranslucentBackground, but it runs after ParserWindow.__init__ has already shown the window (it does whenever the map was open at last quit — the normal case). QWidget re-requests the surface format when the attribute is set, but QWindow::setFormat() after create() does not recreate the window, so the request was never granted: the real app reported alphaBufferSize -1 at launch and 8 only after an Apply. Windows is where it bites, since alphaBufferSize > 0 is what makes Qt create a layered window there while macOS windows always carry alpha; the ordering itself is platform-independent and is asserted as such. #65 is what turned this from cosmetic into total. Its drawBackground writes colour AND alpha with CompositionMode_Source, and a surface with no alpha channel discards the alpha outright, so every value below 100% read as opaque black. The old SourceOver fill accumulated over the previous frame instead — which looked partly see-through at first and drifted toward opaque. That accumulation WAS the ghosting; removing it exposed the missing channel. The attribute moves to _set_flags, which is the thing that (re)creates the native window, so it now runs before every creation rather than before none of them. Nothing in #65 changes: Source composition, the by-hand painter-state restore and the update-mode split all stay, and its six tests still pass. maps.antialias is unaffected — it is read only in MapCanvas.__init__, so it needs a restart rather than a second Apply, and it has no settings UI. Closes #99 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
prokopto-dev
commented
Aug 13, 2026
prokopto-dev
left a comment
Owner
Author
There was a problem hiding this comment.
Approved. The translucent-background attribute is now applied through the virtual flag setup before an initially visible Maps window creates its native surface, and the regression coverage exercises that startup path.
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.
Closes #99.
What the report was
"After the ghosting fix the map background transparency doesn't work at all",
clarified as "it just seemed to start working after clicking Apply twice,
rather than just once". Reported on Windows.
The actual mechanism (the update-mode hypothesis was wrong)
The value is not one Apply late, and the viewport update mode is not the
problem. Traced in the real app, a single Apply does all of this correctly:
apply_backdrop_opacityderives the mode from the freshly clamped newvalue, and it does force a repaint — the whole viewport is repainted on the
first Apply. Both halves of the hypothesis are false.
What was actually wrong: the maps window had no alpha channel, so a
below-100% backdrop had nothing to composite into and read as opaque black at
every setting.
Maps._build_chrome()setWA_TranslucentBackground, but it runs afterParserWindow.__init__has already calledself.show()— which it doeswhenever the map was open at last quit, i.e. normally.
QWidgetre-requeststhe surface format when the attribute is set, but
QWindow::setFormat()aftercreate()does not recreate the window, so the request was never granted:The real app reported
alphaBufferSize = -1at launch and8after oneSettings Apply — because Apply reaches
apply_window_state()→_set_flags()→
setWindowFlags(), which recreates the native window and grants the pendingformat. That is why the backdrop "starts working after an Apply", and why
the number of clicks was fuzzy: it depends on when the recreated window next
repaints.
Windows is where it bites (
alphaBufferSize > 0is what makes Qt create alayered window there; macOS windows always carry an alpha channel), but the
ordering bug is platform-independent and is asserted as such.
Why #65 turned it from cosmetic into total
The missing alpha channel predates #65 (it arrives with the map chrome in
07e37d6). #65 changeddrawBackgroundto composite withCompositionMode_Source, which writes colour and alpha — into a surfacewith no alpha channel that discards it outright. The old
SourceOverfillaccumulated over the previous frame instead, which looked partly see-through at
first and drifted toward opaque. That accumulation was the ghosting #65
fixed; removing it exposed the missing channel underneath.
The fix
One line moves:
WA_TranslucentBackgroundgoes from_build_chrometo_set_flags.setWindowFlagsis the thing that (re)creates the native window,so the attribute is now set before every creation rather than before none
of them.
#65 is untouched
drawBackground'sCompositionMode_Sourcefill, the by-hand painter-staterestore (
DontSavePainterStatemeans Qt does not save/restore around thecall), the antialias-conditional
DontAdjustForAntialiasingand thebackdrop-conditional viewport update mode all stay exactly as they are. Its six
tests in
tests/ui/test_map_chrome.pystill pass.The antialias toggle does NOT have the same shape
maps.antialiasis read only inMapCanvas.__init__, so it is not recomputedfrom a stale value — it is not recomputed at all. Flipping the key and driving
the whole apply path leaves
DontAdjustForAntialiasingunchanged on the livecanvas; only a new canvas (a restart) picks it up. It also has no settings-window
control, so no user reaches it without editing
nparse.config.json. Differentbug shape, not the reported one, left alone.
Tests
Three added to
tests/ui/test_maps_window_chrome.py, on a newmaps_openfixture (
toggled = True, soParserWindow.__init__actually shows the window— which is the moment the platform window is created; the existing
mapsfixture sets it False and so never hit this):
test_the_map_window_is_created_with_an_alpha_channel— fails without the fixtest_the_alpha_channel_does_not_wait_for_a_settings_apply— fails without the fixtest_one_apply_is_enough_to_move_the_rendered_backdrop— drives the applypath once and reads the pixels out of a persistent QImage seeded with the
previous (opaque) frame, reusing the fix(maps): stop the map ghosting when the backdrop is below 100% #65 double-render harness. This one
passed before too, which is the point: it pins the half that was never broken.
Verification
QT_QPA_PLATFORM=offscreen uv run pytest→ 2220 passed, 2 deselected.uv run ruff check . && uv run ruff format .→ clean.Driven end-to-end against the real app offscreen (the
verifyskill), readingthe composited window rather than a forced re-render:
viewport().repaint()Risks / follow-ups
parsers/discord.pysetsWA_TranslucentBackgroundafterParserWindowhas shown its window too, so it has the same latent ordering bug. Left out
of this PR to keep the diff to the reported surface; worth its own issue.
🤖 Generated with Claude Code