fix(gui): relabel the split badge from the slice role on every update (#4051)#4262
Conversation
…aethersdr#4051) — Principle II. The split/swap badge is one QPushButton whose click handler dispatches on its text, but updateSplitBadge() only ever wrote "SWAP" (TX branch) — the text was one-way for the widget lifetime. After split teardown the badge was re-styled to idle but kept the stale "SWAP", so every click emitted swapRequested() and Split could never be re-enabled from the flag. The same staleness mislabeled the RX-split member "SWAP" after the transient TX handoff during split creation. Relabel unconditionally from the current role at the top of the function: isTxSlice ? "SWAP" : "SPLIT". Styling untouched. Verified via the Automation Bridge on a FLEX-6700 (RX-only): scripted split -> teardown A/B; pre-fix main (8c973d7) fails both asserts (badge stuck SWAP), this branch passes all. Co-authored-by: Don @ cloaked.agency <don@cloaked.agency>
There was a problem hiding this comment.
Clean, correct, and well-scoped. I traced the root cause independently: the click handler (VfoWidget.cpp:686) dispatches on m_splitBadge->text(), and pre-fix the only setText after construction was "SWAP" in the TX branch — a one-way latch, exactly as described. Relabeling unconditionally from isTxSlice at the top of updateSplitBadge() reconciles the dispatch text to the model state on every pass, mirroring what the styling already does. m_splitBadge is constructed unconditionally in the ctor, so no null risk; the derivation also fixes the post-swap RX-member mislabel you called out. No convention issues (no QSettings/AppSettings, no leaks), and the live A/B transcript on the 6700 is convincing. The unit-test note is a reasonable call given the widget's GUI dependency closure.
Nothing blocking. Nice fix — thanks for the thorough write-up.
Non-blocking note:
- The badge's
accessibleName/accessibleDescription("Split mode" / "Toggle split transmit frequency", set once atVfoWidget.cpp:1288-1289, outside this diff) don't track the SWAP state, so a screen reader announces "Split" while the button acts as Swap. Pre-existing and out of scope here, but if you touch this again it'd be a natural companion fix to derive them from the same role.
🤖 aethersdr-agent · cost: $4.6459 · model: claude-opus-4-8
jensenpat
left a comment
There was a problem hiding this comment.
Reviewed the exact head and its clean combined merge with current main. updateSplitBadge() already receives the model-derived role, and resetting the dispatch text on every update fixes both teardown and post-swap stale state without changing styling or protocol behavior. Full AetherSDR build passed with 8 parallel jobs; PR CI and live FLEX-6700 A/B automation are green. No blockers.
Summary
The split/swap badge in the VFO flag is one
QPushButtonwhose click handler dispatches on its text (VfoWidget.cpp~687).updateSplitBadge()sets"SWAP"in its TX-slice branch — and that is the onlysetTextafter construction, so the text is one-way for the widget's lifetime. After the split pair is torn down,MainWindow::updateSplitState()correctly re-styles the badge to the ghosted idle look, but the stale"SWAP"text keeps every click emittingswapRequested()instead ofsplitToggled()— Split can never be re-enabled from the flag without reconnecting.This change relabels the badge unconditionally from the slice's current role at the top of
updateSplitBadge():isTxSlice ? "SWAP" : "SPLIT".Why
"SWAP"text under the red RX-split style — mislabeled and misdispatching.Scope
src/gui/VfoWidget.cpp(updateSplitBadge())Constitution principle honored
Principle II — The Radio Is Authoritative On Live State. The badge's dispatch text is client-remembered state; before this fix it could disagree with the radio's actual split state (no split pair active) and the widget kept trusting its remembered value. The fix re-derives the label from the reconciled model state on every
updateSplitState()pass, so the UI mirrors the radio instead of its own history.Test plan
ninja -C buildclean on Linux (Nobara 43, Qt 6.10.3, gcc 15.2.1)assert SPLIT → click badge → assert SPLIT(rx)+SWAP(tx) → remove the created slice → assert SPLIT) run against pre-fixmain(8c973d7b) and this branch:8c973d7bSPLIT, TX badgeSWAPSWAPSPLITSWAP(#4051)A/B transcript (badge text read from the live widget tree via
dumpTree; slice teardown scoped to the slice the script itself created)grab_widgetPNGs captured at each step (before / mid-split / after) on both builds — the after-teardown pair shows ghostedSWAP(base) vs ghostedSPLIT(fix) on the same flagNote on unit tests:
VfoWidgetcompiles only into theAetherSDRexecutable target (GUI_SOURCES), notaethercore, so a standalone badge test would need to enumerate the widget's full GUI dependency closure as a new test target — disproportionate for a one-line relabel. The existingvfo_flag_placement_testdeliberately covers only the class's static helpers for the same reason.Checklist
Closes #4051
73, Ozy K6OZY
AI compute partnership: cloaked.agency — (model: claude-fable-5)