feat(cw): resizable CW decode panel + adjustable, persistent font size (#3628)#3824
Conversation
…ze (aethersdr#3628) The CW decode panel was locked to a hardcoded 80px height (setFixedHeight) with a hardcoded 13px monospace font and no persistence — hard to read on high-DPI displays and unable to show more decoded-text history. The full scrollback was already retained, so the only blockers were the fixed height and font. Changes (all client-side, no protocol/decoder changes): - Drag-grip (`cwResizeGrip`) along the panel's top edge resizes it vertically, clamped 60–600px. A thin strip rather than a QSplitter so the GPU SpectrumWidget (QRhiWidget) is never reparented. - A-/A+ toolbar buttons (`cwFontDown`/`cwFontUp`) and context-menu "Increase/Decrease font size" actions adjust the decoded-text font, clamped 8–32px. - Both font size and panel height persist in the existing nested `CwDecoder` settings blob (CwDecodeSettings), restored on launch. - The panel gains objectName `cwDecodePanel` so the automation bridge can address and read it. Taller panel immediately reveals more already-decoded history; existing decode functionality is unchanged. Proven with the agent automation bridge: - dumpTree after seeding panelHeight=220 → `cwDecodePanel` geometry.h reads back 220 (was hardcoded 80): persistence + restore-on-launch. - invoke cwFontUp ×3 → on-disk CwDecoder blob fontPx 13→16: the font control is reachable, increments correctly, and persists. Fixes aethersdr#3628 💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat
There was a problem hiding this comment.
Reviewed the diff against the existing PanadapterApplet and CwDecodeSettings code — this is clean and well-scoped. Thanks @jensenpat. 🙏
Conventions ✓
- Persistence stays in the nested
CwDecoderJSON blob viaCwDecodeSettings(Principle V) — no new flat keys. ensureToggles()correctly only fillsrx/txwhen absent, so a font/height write never clobbers a user's existing enable state.- Clamps are consistent between the constructor restore (
std::clamp(..., 8, 32)/60, 600),adjustCwFont, andsetCwPanelHeight, so an out-of-range persisted value can't escape. - The grip-vs-
QSplitterchoice (avoiding aQRhiWidgetreparent, cf. #1096) is the right call and well-justified.
Correctness ✓
- The grip event-filter press/move/release uses
globalPosition()deltas, and Qt's implicit mouse grab on press keeps move/release routed tom_cwGripeven as the cursor leaves the 4px strip — so the drag tracks correctly. - Persisting only on release (not per mouse-move) is the right tradeoff.
applyCwFont()runs.arg(m_cwFontPx)on the template beforeapplyStyleSheetsubstitutes{{color.*}}, and there are no stray%tokens — no format collision.
One minor, optional note (non-blocking)
The new grip branch in eventFilter returns early on MouseButtonPress, which skips the emit activated(m_panId) at the bottom of the function. So grabbing the resize grip won't mark that panadapter as the active pane the way clicking elsewhere on it does. Probably immaterial in practice, but if you want a press on the grip to also focus the pane, emitting activated(m_panId) before the return true would match the rest of the widget's behavior.
The acknowledged gap — no drag-gesture synthesis in the automation bridge, so the literal grip drag wasn't exercised end-to-end — is a fair and clearly-stated limitation; the persistence/restore and font paths are well proven. No changes required from my side.
🤖 aethersdr-agent · cost: $2.5825 · model: claude-opus-4-8
ten9876
left a comment
There was a problem hiding this comment.
Clean, well-implemented. Verified: the drag-grip eventFilter direction/clamp is correct (dy = startY - currentY enlarges the bottom panel on up-drag, clamped 60–600) and persists only on release; adjustCwFont clamps 8–32 + early-returns + persists, applyCwFont re-bakes the stylesheet; persistence is Principle-V clean (nested CwDecoder blob) and ensureToggles only adds rx/tx if absent so it never clobbers an existing toggle value; restore-on-construct applies both keys at build. Good call using a grip strip rather than a QSplitter to avoid reparenting the GPU SpectrumWidget (#1096). No protocol/decoder/TX surface, addressable for the bridge. Thanks @jensenpat.
Summary
Makes the CW decode panel vertically resizable with an adjustable, persistent font size — closing #3628.
Previously the panel was locked to a hardcoded
setFixedHeight(80)with a hardcoded13pxmonospace font and no persistence, which is hard to read on high-DPI displays and can't show more decoded-text history. The panel already retains full scrollback (m_cwTexthas nosetMaximumBlockCount/trimming), so the only blockers were the fixed height and font — no decoder changes are needed.Changes (all client-side; no FlexLib/protocol/decoder changes)
cwResizeGrip) along the panel's top edge resizes it vertically, clamped 60–600px. A grip strip rather than aQSplitterdeliberately: wrapping the layout in a splitter would reparent the GPUSpectrumWidget(QRhiWidget), which carries native-window risk (cf. Mouse cursor disappears when hovering over panadapter, waterfall, and CW decoder panes (Windows 11) #1096). The grip leaves the spectrum surface untouched.A-/A+buttons (cwFontDown/cwFontUp) in the CW toolbar and "Increase/Decrease font size" actions in the decode-text context menu, clamped 8–32px.fontPxandpanelHeightare stored in the existing nestedCwDecodersettings blob viaCwDecodeSettings(constitution Principle V) and restored in the applet constructor on launch. A newensureToggles()helper keeps the rx/tx enable state intact when only a display key changes.objectName "cwDecodePanel"so the agent automation bridge can address and read it.A taller panel immediately reveals more already-decoded history. Existing decode functionality is unchanged.
Files
src/gui/PanadapterApplet.cpp/.h— grip + drag handling ineventFilter, font buttons/menu,applyCwFont/adjustCwFont/setCwPanelHeight, restore on construct.src/gui/CwDecodeSettings.h—fontPx()/panelHeight()getters + setters.How the agent automation bridge proved it
RX-only proof (no TX), against a live FlexRadio panadapter on the
fix/3628-cw-resizebuild:1 — Panel-height persistence + restore-on-launch. Seeded
panelHeight=220into the settings file, launched, and read the tree back:The panel height read back 220 (the hardcoded value was 80) — the persisted height is honored on launch.
2 — Font control reachable + adjusts + persists. Invoked the new button via the bridge three times and watched the on-disk
CwDecoderblob:fontPxwent 13 → 16 (three +1 increments) and persisted to disk.dumpTree also confirmed
cwFontUp,cwFontDown, andcwResizeGripare present in the applet tree. The radio was left untouched (RX-only) and the local settings file was restored afterward.Agent automation bridge — gap found
invokebutton-style actions but cannot synthesize a press-move-release drag, so the resize grip drag itself could not be exercised end-to-end through the bridge. I proved the persistence/restore path (seed →dumpTreeheight read-back) and the font path (invoke→ disk), but not the literal drag gesture. Proposed: adrag <target> dx dy(ormouse <target> down/move/up) verb so grip/slider-handle drags become provable.Fixes #3628
💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat