[ux] Fix RX applet frequency font scope on restart#4267
Conversation
There was a problem hiding this comment.
Traced this through ThemeManager and it holds up. RxApplet declares its applet/rx scope in the constructor (RxApplet.cpp:320) before buildUI(), and applyStyleSheet resolves tokens via resolveFor → containerPathFor, which walks the widget's Qt parent chain and re-resolves on QEvent::ParentChange (ThemeManager.cpp:1709). The freq label/editor are styled while unparented and then reparented into the stack by addWidget; before this change the stack itself had no parent, so that re-resolve dead-ended at root scope and font.family.freq fell back to the root token — exactly the restart symptom in #4159. Parenting the stack to this gives the child chain a path up to applet/rx, and the scoped override is the one already covered by theme_manager_test. The root-cause writeup matches the code.
Change is minimal, uses standard Qt parent-ownership, and is confined to the RX frequency hierarchy — no null/leak/settings concerns. Nice, well-targeted fix.
Nothing blocking. Thanks for the clear root-cause narrative — it made verification straightforward.
Non-blocking note: the fix leans on the ParentChange-triggered re-resolution (the styles are still applied to the label/editor while they're unparented, and only land in scope once addWidget reparents them). That's precisely how the tracking is designed to work, so it's fine as-is — just worth knowing that the first resolveFor pass still runs at root scope and the event filter is what makes it correct.
🤖 aethersdr-agent · cost: $4.6212 · model: claude-opus-4-8
NF0T
left a comment
There was a problem hiding this comment.
Verified the fix mechanism directly against ThemeManager::containerPathFor/applyStyleSheet (src/core/ThemeManager.cpp:1668-1724,1853-1869), not just the PR's narrative: the critical moment is m_freqStack->addWidget(m_freqLabel/m_freqEdit) (RxApplet.cpp:585,596), which fires each child's one-shot QEvent::ParentChange re-resolution before m_freqStack itself is later reparented via layout activation (:636-637). Pre-fix, m_freqStack was unparented at that moment, so the ancestor walk dead-ended at root scope — exactly the #4159 symptom. Post-fix, m_freqStack already has this as parent, so the walk finds the applet/rx scope immediately. The fix is correct.
CI green across all 7 checks. Commit signed.
Two non-blocking notes for a future pass, not blockers here:
- Since
m_freqStack's explicit parent (this) differs from its eventual layout-activation parent (inner, which is itself already parented tothisearlier in the same function), Qt's layout system will emit a harmless "widget in wrong parent; moving to correct parent" warning on everyRxAppletconstruction. Parenting toinnerdirectly instead ofthiswould give the identical fix without the warning. - This exact pattern (container built + children styled before the container itself is attached to the real widget tree) recurs elsewhere:
VfoWidget.cpp's ownm_freqStack/m_digOffsetStack(the PR's own comment cross-references VfoWidget as sharing this token), and — more urgently —RxApplet.cpp's ownm_agcContainer/m_agcTSlider, wherecolor.slider.foregroundis scope-overridden (applet/rxgreen vs. root blue), meaning the AGC threshold slider is rendering the wrong color at every startup today, live, right now, unrelated to this PR. Worth a follow-up issue — recommend filing one for the AGC slider specifically since it's a currently-visible defect with no tracking issue yet.
Approving — Tier 3 only (src/gui/RxApplet.cpp), no Tier 2/1 gate to satisfy.
Summary
font.family.freqtokenFixes #4159.
Related context: #3653 and #3688 fixed custom-theme serialization/loading, but the persisted scoped token still resolved incorrectly while the RX applet was being constructed.
Root cause
RxAppletregistered the frequency label and editor styles while theirQStackedWidgethad no parent.ThemeManagertherefore walked an incomplete ancestor chain and resolved the root frequency token at startup. Qt reparented the stack later during nested-layout activation, but that ancestor-only parent change did not reach the already-registered children. A live theme edit reapplied every style after the tree was complete, which is why the saved font appeared to work until the next launch.Fix
Construct the frequency stack with
RxAppletas its explicit parent before registering either child stylesheet. Both widgets can now resolve theapplet/rxscope immediately; later layout placement remains unchanged.Validation
cmake --build build -j8: passedtheme_manager_test: 1/1 passedtools/check_a11y.py: passed (8 existing warnings outside this diff)tools/check_engine_boundary.py --strict: passed, 0 blockersgit diff --check: passedAutomation bridge proof
The app was launched disconnected with auto-connect disabled. A proof-only Default Dark fixture set
applet/rxfrequency text to Courier New at 12 px, making scoped resolution unmistakable; the fixture was removed before the final build and commit.Before the fix, startup rendered the root DSEG frequency font and selecting Default Dark again changed it to the scoped Courier font. After explicitly parenting the stack, the immediate-startup capture and same-theme-reapply capture were byte-for-byte identical (
SHA-256 449a45f21b1aed4f5014b90cf23a190cb0f3fb08ff1b51dd77884323f1fb5c0f). No radio connection or transmit action occurred.Before: persisted scoped font missed at startup.
After: scoped font is already active at startup and remains unchanged after live reapply.
💻 Generated with Codex with architecture by @jensenpat
👨🏼💻 Generated with OpenAI Codex (GPT-5.6 Sol) and tested by @jensenpat