feat(smeter): config context menu + needle pivot cover with backlight glow#3859
Conversation
The S-meter applet carried two rows of inline controls (TX Select + RX Select combos; Peak Hold button + Decay combo + RST), which crowded the compact VU applet. Move all of them into a right-click context menu on the gauge so the applet body is just the meter. The menu has five labelled sections — TX Select (Power/SWR/Level/Compression), RX Select (S-Meter/S-Meter Peak), Peak Hold (Enabled toggle), Decay speed (Fast/Medium/Slow), and Reset Peak Hold — built on customContextMenuRequested. Exclusive groups use QActionGroup. Section headers are disabled QWidgetAction + styled QLabel rows, because QMenu::addSection() text doesn't render under the app's menu styling (only its separator shows). No behavior change: the menu reads/writes the same AppSettings keys (SMeter_TxSelect, SMeter_RxSelect, PeakHoldEnabled, PeakDecayRate) and calls the same SMeterWidget methods; startup restores saved state directly onto the gauge. The now-unused m_txSelect/m_rxSelect members are removed. Verified via the automation bridge: inline controls gone, gauge intact, no crash. Right-click menu (incl. section headers) confirmed visually. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…glow Hide the VU needle's origin behind a filled half-disc at the bottom-centre of the gauge (a near-black moulding with a subtle glossy rim), like the pivot bump on a classic analog VU meter, so the needle appears to emerge from under it. Add a warm radial backlight glow behind the cover — the cover masks the bright centre, leaving a soft, diffuse amber halo spilling out from behind the moulding, as if a lamp sits behind the mask. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…bsystem The pivot moulding, its rim, and the backlight glow were hardcoded. Move them to theme tokens so they follow the active theme and are customizable: - color.meter.pivot.fill — moulding - color.meter.pivot.rim — glossy rim - color.meter.pivot.glow — warm backlight (paint applies the gradient alphas) Registered in the ThemeManager paint-code defaults and both bundled themes (default-dark / default-light, the latter tuned for a light face). SMeterWidget reads them via ThemeManager::color(); dark-theme values are unchanged, so the render is identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Nice work — this is a clean, well-scoped change and both halves look correct. CI is green across the board (build, CodeQL, Qt Accessibility static analysis, macOS/Windows). Thanks for the thorough PR description and the #3858 cross-reference explaining the addSection() workaround.
What I checked
Conventions — ✅ Uses AppSettings (not QSettings) throughout; reads/writes the same four keys the inline controls used, so persisted state migrates transparently. The startup-restore block correctly qBounds the saved indices before indexing kTxMeterItems/kRxMeterItems, which is actually more robust than the old if (idx < count) guards.
Memory/ownership — ✅ No leaks. QMenu menu is stack-allocated; the QActionGroups and QWidgetActions are parented to &menu, and each header QLabel is owned by its QWidgetAction via setDefaultWidget. Everything tears down when the menu falls out of scope after exec().
Paint code — ✅ cx/h/w are all in scope (defined at the top of paintEvent), the radial-gradient stops are monotonic (0 → edge → mid → 1), and the cover is drawn after the needle so the needle reads as emerging from under the moulding. Good call seeding color.meter.pivot.{fill,rim,glow} in seedBuiltinDefaults() so a missing theme file degrades gracefully rather than painting transparent.
Removed members — ✅ m_txSelect/m_rxSelect removal is consistent: header decls and every .cpp reference go together in the same diff, no danglers.
Minor (optional, non-blocking)
- The pivot glow/cover re-fetches three theme tokens and rebuilds the
QRadialGradienton everypaintEvent. It's negligible next to the existing tick/gradient work in this paint path, so only worth caching theQColors as members if you happen to touch this again — not worth a revision on its own.
Looks good to merge from my side. 👍
🤖 aethersdr-agent · cost: $2.6484 · model: claude-opus-4-8
Summary
Two VU/S-meter changes:
Config context menu
Right-click the S-meter → a
QMenuwith five labelled sections:Exclusive groups use
QActionGroup. Section headers are disabledQWidgetAction+ styledQLabelrows, becauseQMenu::addSection()'s text doesn't render under the app's menu styling (only its separator shows) — see #3858 for the bridge follow-up.No behavior change: the menu reads/writes the same AppSettings keys (
SMeter_TxSelect,SMeter_RxSelect,PeakHoldEnabled,PeakDecayRate) and calls the sameSMeterWidgetmethods; startup restores saved state directly onto the gauge. Removed the now-unusedm_txSelect/m_rxSelectmembers.Pivot cover + glow
Test plan
cmake --build build --target AetherSDR)Note
UX change (controls → context menu) + visual change (pivot cover/glow); both at maintainer direction.
🤖 Generated with Claude Code