[ux] Make the VFO DSP label indicate active noise reduction#4241
Conversation
There was a problem hiding this comment.
Nice, focused change — the state machine reads cleanly and coverage of the "closed panel erased the cue" paths (closeActiveTab, showTab toggle-off / switch-away, setCollapsed expand, radio-DSP and client-NR changes, syncFromSlice) looks complete. I especially like that swapping the opacity effect for hide()/show() also sidesteps the known QWidget::render()-can't-rasterize-QGraphicsOpacityEffect problem the GPU flag-sprite path has (see the makeOptLabel comment) — the level row will now render correctly in the spectrum overlay. Verified: all SliceModel accessors exist, DSP tab index 1 matches the new dspTabButton object name, no stray QGraphicsOpacityEffect references remain after the include removal, closeActiveTab's early-return keeps the new closedTab indexing in bounds, and visibilityChanged is correctly sampled before setVisible. All 7 checks green, commit signed.
Non-blocking notes
- The aggregate flags
anfOn/anflOn/anftOn(auto-notch) andapfOn(audio peak filter) but announces "noise reduction active" — a notch/peak filter isn't NR, so a screen-reader user enabling only ANF/APF hears a slightly inaccurate label. Consider narrowing the set to true NR flags, or relabeling to "DSP active". Judgment call; fine to keep as-is if the green cue is intentionally "any DSP engaged."
🤖 aethersdr-agent · cost: $5.7974 · model: claude-opus-4-8
NF0T
left a comment
There was a problem hiding this comment.
Focused, well-scoped fix for a real gap (#4237) — the closed-tab "no visible DSP cue" problem is solid work, and updateDspTabAccent()'s wiring into all the right signal paths (radio DSP toggles, client AetherDSP, tab close/collapse/expand, slice sync) is thorough. CI is green across all 7 checks and the commit is signed.
Two technical issues and one process note before this can merge:
1. The tab accent surfaces DSP state for controls the mode hides. syncFromSlice() (VfoWidget.cpp:4526-4574) hides m_nrBtn/m_nbBtn/m_nrlBtn/m_anfBtn/m_anflBtn/m_anftBtn in FM mode — the project's own design treats those six functions as not meaningful there. updateDspTabAccent() is called immediately after syncDsp() but before that mode-gating runs, and it reads the raw model flags with no mode awareness. If any of those six is true on an FM slice, the tab now shows a persistent green "active" cue for a function the user can't see or reach. Please gate the aggregate on the same mode logic (or exclude FM-inapplicable flags), so the indicator never claims something is active when its control isn't even shown.
2. "Noise reduction active" mislabels notch/peaking filters. anfOn/anflOn/anftOn are notch filters and apfOn is a CW audio peaking filter (per their own tooltips at VfoWidget.cpp:1836-1843) — none of these are noise reduction. A screen-reader user who enables only the auto-notch hears "noise reduction active," which is inaccurate. Either narrow the aggregate to the true NR flags (nb/nr/nrl/nrs/rnn/nrf) for the accessible name, or relabel to something mode-neutral like "DSP active" if the intent is genuinely "any DSP engaged."
3. Small process note. This one touches AGENTS.md's Autonomous Agent Boundaries section, which reserves visual/UX design calls (color scheme, aggregation scope, sizing) for maintainer sign-off, with a PR note when an agent's implementation reaches into that territory. I know this was your call with your tooling, Pat, not the agent freelancing — but the PR body doesn't currently show that the color scheme/aggregation choices were checked against #4237's ask (which didn't specify either). Not asking for a redesign — just flagging it so @ten9876 can give a quick nod on the design choices before merge, per the project's own escalation path for exactly this situation.
Smaller cleanup, non-blocking, worth doing in this pass since the diff already touches these spots:
updateDspTabAccent()fires on every one of the 10 individually-wired DSP signals;SliceModel::applyChanges()emits 9 of them unconditionally on a single status packet (SliceModel.cpp:1170-1204), so one radio update can re-run this up to 9x back-to-back, and unlike the accessible-name check five lines above it, thesetStyleSheet()call has no before/after guard.- The
if (closedTab==1) updateDspTabAccent(); else setStyleSheet(kTabLblNormal);pattern is copy-pasted three times (closeActiveTab, both branches ofshowTab) with an easy-to-miss precondition (m_activeTabmust already be-1). A smalldeactivateTabButton(int)helper would remove the duplication. setObjectName(QStringLiteral("dspTabButton"))is added but never referenced anywhere else in the repo — looks like scaffolding for a hook that didn't get wired in.
Happy to re-review quickly once 1–2 are addressed and 3 has a maintainer nod — this is close.
|
@NF0T, both requested fixes are addressed in signed commit
Jeremy, the project owner, approved the UX design call. Validation: full macOS build, Ready for re-review. |
aethersdr#4241) Three non-blocking review nits from @NF0T: - updateDspTabAccent()'s setStyleSheet() now skips the repaint when the target style is unchanged, matching the accessible-name guard above it. One radio status packet re-runs this up to ~9x (SliceModel::applyChanges emits the DSP signals unconditionally), so the unguarded style recompute was wasted work on a per-packet path. - Extract deactivateTabButton(int) to replace the three copy-pasted "reset a just-closed tab button" blocks in closeActiveTab() and both showTab() branches, centralizing the "m_activeTab already cleared" precondition. - Drop the unused setObjectName("dspTabButton") scaffolding (no consumer in the repo). No behavior change. Full app builds clean; offscreen smoke-launch constructs the VFO widget without asserts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ten9876
left a comment
There was a problem hiding this comment.
Maintainer approval (Tier 1) — nits fixed, design signed off
Full high-effort pass. Both of @NF0T's technical blockers are genuinely resolved in 9bdd89ac:
- #1 (accent surfaces mode-hidden controls):
activeWhenAvailable()'s!button->isHidden()guard correctly excludes FM-hidden DSP buttons (they're explicitlysetVisible(false), soisHidden()is true) while still counting panel-closed buttons (ancestor hidden, own flag untouched) — exactly the distinction the fix needs. - #2 (mislabeled notch/peak as "noise reduction"): the accessible name is now mode-neutral "DSP active".
I also confirmed the refactored closeActiveTab/showTab keep the m_activeTab < 0 guard (no m_tabBtns[-1]) and that all three isFm sites now include DFM (this PR fixes a pre-existing gap in syncFromSlice). No correctness defect.
I pushed c28605f3 to clear @NF0T's three non-blocking cleanup notes:
- Guard
updateDspTabAccent()'ssetStyleSheet()on an unchanged-style check (it re-runs up to ~9x per status packet). - Extract
deactivateTabButton(int)to dedupe the three copy-pasted tab-reset blocks. - Remove the unused
dspTabButtonobject name.
Built clean + offscreen smoke-launch (no asserts). No behavior change from those three.
Design sign-off (NF0T process note #3): as the Tier-1 maintainer I'm approving the visual/UX calls this PR makes — the green (#20a040) persistent closed-panel DSP cue, the aggregate-any-DSP scope, and the opacity→hide() level-row change (which also fixes the real QGraphicsOpacityEffect render bug in the spectrum overlay). Good work, @jensenpat; thanks for the thorough signal wiring and the careful review, @NF0T.
Summary
dspTabButtonobject name and a dynamic accessible nameRoot cause
The top-level DSP tab only had two visual states: cyan while its panel was open and grey after every close/collapse path. Radio DSP state changes and the existing aggregated AetherDSP state never updated that tab, so closing the panel erased the only visible cue.
The layout issues had separate concrete causes: radio-side buttons use a fixed 26 px height while ADSP/AetherVoice requested only a 22 px minimum, and the opacity-zero DSP-level slider remained in the layout even when it had no target.
Implementation
VfoWidget::updateDspTabAccent()aggregates all per-slice radio DSP flags with the existing global AetherDSP-active flag. It updates the closed-tab style and accessible name on initial slice sync, every radio DSP state change, every client NR state change, tab closure, and expanded-state restoration. The currently open DSP tab remains cyan so open versus active are visually distinct.The client launchers now use the same fixed 26 px height as the radio buttons. The shared level row is explicitly shown only for a leveled active DSP, with the VFO flag refit after visibility changes.
Testing
cmake --build build -j8— passgit diff --check— passpython3 tools/check_engine_boundary.py --strict— 0 blocking findingspython3 tools/check_a11y.py— warning-only existing repository findingsRelated
PR #3822 added the inner ADSP launcher cue for client-side NR. This change completes the always-visible top-level DSP indication and adds radio-side DSP coverage.
Fixes #4237
👨🏼💻 Generated with OpenAI Codex (GPT-5.6 Sol) and tested by @jensenpat