fix(gui): RX Controls hero frequency + never-clip shrink-to-fit (#3463)#3839
Conversation
…ethersdr#3463) The frequency readout in the VFO slice flag (VfoWidget) and the RX Controls box (RxApplet) is a plain QLabel styled by the theme's font.size.freq / font.family.freq tokens (DSEG7 Modern, 20px dark / 26px light), poured into a fixed-width box with no shrink-to-fit and no elision. When the rendered string is wider than the box, the right-aligned label silently clips its leading (most-significant) digits — the operator loses the MHz, the worst digits to lose: - RxApplet: the freq stack shares its row with TX/mode/WFM inside the fixed ~248px AppletPanel column; the leftover (~124px measured live) is narrower than even "21.074.000" at the default 20px, so the leading "2" clips on the DEFAULT DARK theme. In light theme (26px) only "074.000" survives. - VfoWidget: the 252px flag fits 2-digit-MHz HF in dark, but the light-theme 26px digits (and the wider Windows metrics in aethersdr#3463) overflow and clip the leading digit. Fix: drop `font-size` from each freq label's QSS and drive the pixel size via setFont() with a width-fitted size capped at the theme nominal (new FreqLabelFit.h helper, shared by both surfaces). A QSS font-size OVERRIDES setFont(), so omitting it is precisely what lets the fit take effect (verified empirically). Re-fit on text update, label QEvent::Resize, and ThemeManager::themeChanged so Dark<->Light and Theme-Editor font edits stay correct. RxApplet's freq stack is made Expanding (the stretch is removed) so it fills a font-independent width, avoiding a fit<->reflow oscillation. The digits stay full-size whenever they fit and shrink only as much as needed to keep the leading digits on screen. Proven with the agent automation bridge (tune + grab on a FLEX-8400M, RX-only, before/after, both themes): - RxApplet dark 21.074 MHz: leading "2" clipped -> full "21.074.000" - RxApplet dark 144.2 MHz: "1" gone, "4" clipped -> full "144.200.000" - RxApplet light 21.074 MHz: only "074.000" -> full "21.074.000" - VfoWidget light 21.074 MHz: leading "2" clipped -> full "21.074.000" - VfoWidget dark (already fit): unchanged, no regression Fixes aethersdr#3463. Refs aethersdr#3515 (the clipping is fixed; the "make the digits larger" ask there remains a separate enhancement). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Thanks for this @jensenpat — clean, well-scoped fix with a clear root-cause writeup and before/after proof. The QSS-font-size-overrides-setFont() finding is the crux and the design around it is sound. The diff reads well: null/empty/availW<=0/maxPx<=0 are all guarded, and making RxApplet's freq stack Expanding (so the fit width is font-independent) correctly forecloses the fit↔reflow oscillation. All five files serve the one fix, no scope creep, no settings/RAII concerns.
A couple of non-blocking notes:
1. Linear scan on a hot path. fitFreqPixelSize() walks maxPx → minPx constructing a fresh QFontMetrics and measuring horizontalAdvance() each step (~10–16 iterations), and applyFreqFit() runs on every updateFreqLabel() — i.e. on every frequency tick during tuning/scrolling, plus every resize. Glyph advance is monotonic in pixel size, so a binary search over [minPx, maxPx] would cut this to ~4 measurements with identical results. Given how perf-sensitive the spectrum/VFO path is here, might be worth the small change. Not a correctness issue.
2. Possible mojibake in a comment. The diff renders a stray replacement-style character in FreqLabelFit.h ("the theme's nominal size ��� it only shrinks…") where an em-dash looks intended. This may just be diff-transport mangling of the multi-byte char (the pre-existing box-drawing context lines show the same artifact), but worth a quick confirm that the committed file is clean UTF-8.
Behavior-wise this looks correct and the re-fit hooks (text update, QEvent::Resize, themeChanged) cover the Dark↔Light and Theme-Editor cases. Nice work.
🤖 aethersdr-agent · cost: $3.7019 · model: claude-opus-4-8
|
Five files, one new header-only helper, clean fix for a genuinely bad UX bug (operator loses MHz digits in both themes on their own radio display). Root cause is correctly and precisely diagnosed. The key Qt behavior — a QSS The implementation is correct on all three mechanisms:
All three re-trigger paths are wired: text update (both LOCKED and normal freq paths in CI: All 6 checks pass — build, check-macos, check-windows, a11y, CodeQL (quick), and analyze (cpp) (39m55s, just completed). The live-radio Constitution: Principle XI (Fixes Are Demonstrated — before/after bridge grabs on both surfaces, both themes, two frequencies including the 3-digit-MHz 144 MHz case, explicit regression check on VfoWidget dark) and Principle VIII (QSS/setFont interaction verified empirically, not assumed) both honored.
|
NF0T
left a comment
There was a problem hiding this comment.
Root cause confirmed empirically (QSS font-size overrides setFont — verified). QSS change, FreqLabelFit helper, and Expanding size policy all correct; reflow oscillation prevention is the subtle piece and it's handled. All three re-trigger paths wired (text update, QEvent::Resize, themeChanged). All 6 CI checks pass including analyze (cpp) at 39m55s. Tier 3 only.
Squash subject: fix(gui): shrink-to-fit VFO/RxApplet frequency so digits never clip — Principle XI. (#3839)
…ethersdr#3839 follow-up) Follow-up to aethersdr#3839. That change stopped the frequency clipping but drove the fitted pixel size via QWidget::setFont() — which a QLabel whose stylesheet names font-family silently discards on every re-polish, so the digits actually rendered at the ~13px app default ("the frequency is tiny by default"). This applies the fitted size through the stylesheet as a literal font-size (which always wins over the cascade), and reorganises the RX Controls panel around it. - FreqLabelFit.h keeps fitFreqPixelSize(); the size is now applied via the label's QSS (literal font-size), with colour/family still resolved from theme tokens and the last size cached to avoid a re-polish on every tune step. Each freq box is a stable, font-independent width so the fit can't oscillate. - Hero frequency: its own full-width row at ~34px, with the mode selector and the TX/WFM toggles on a compact context strip beneath it. The band and filter-bandwidth labels are dropped (the band is obvious from the large frequency; bandwidth is already shown by the lit preset button + passband graph). - Volume and AGC are promoted to full-width sliders directly under the mode selector; STEP moves into the right control group with balance (pan) and squelch. Net top-to-bottom flow: frequency, mode/TX/WFM, volume, AGC, then a two-column grid (filter presets + passband | STEP, pan, SQL, RIT, XIT). - VfoWidget flag: same literal-QSS sizing at the theme nominal. Proven live on a FLEX-8400M via the agent automation bridge (tune + grab, before/after, both themes): RX Controls 21.074 MHz shows the full "21.074.000" at ~34px, 144.2 MHz shows "144.200.000" with no clip, and the VFO flag is unchanged. Refs aethersdr#3515, aethersdr#3463 (aethersdr#3839 follow-up). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reverts #3839. #3839 landed half-baked. It drove the fitted frequency pixel size via `QWidget::setFont()`, but a `QLabel` whose stylesheet names `font-family` makes Qt recompute the font from the QSS on every re-polish and **discard `setFont()`**, falling back to the ~13px app default. So the leading digits no longer clip, but the frequency renders **unreadably small** — the fix is incomplete. Rather than ship that behavior on `main`, this reverts it. The **complete** fix is **#3844**: - the fitted size is applied through the stylesheet as a literal `font-size` (which always wins over the cascade), so the digits actually render at the intended size; - the RX Controls frequency becomes a full-width hero (~34px) with a reorganized control layout. Reverts cleanly — no commit since #3839 touched the frequency files. Build passes. 💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… don't clip The WFM software-demod toggle lived in RxApplet's `m_freqRow` — the same row as the large frequency readout — so at narrower applet widths it (with the mode combo) stole width and clipped the digits. The earlier shrink-to-fit (#3839) only masked the symptom. Relocate it to the DAX pan overlay menu, directly below the "IQ Ch" selector — the natural home, since WFM demodulates that pan's DAX IQ stream on the PC. The toggle is slice-keyed (acts on the menu's slice) but always reachable (unlike the FM-mode-gated VFO-flag button), so dropping the always-visible RxApplet button doesn't cost reachability. - RxApplet: drop m_wfmButton (creation + member) and the vestigial setWfmActive(); keep the mode-combo's wfmActivated(false) teardown so a real-mode selection still tears down a running demod. - SpectrumOverlayMenu: add the WFM toggle below IQ Ch; emit wfmToggleRequested(on, sliceId); add a self-gated setWfmActive() reflector. - MainWindow_Wiring: route the toggle to activateWFM/deactivateWFM with the same deactivate guard as the flag button. - reflectWfmButtons: keep the VFO flag and the DAX-menu toggle in sync. WFM remains a per-slice singleton; this only changes where the toggle lives. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gits The WFM software-demod toggle lived in RxApplet's `m_freqRow` — the same row as the large frequency readout — so it clipped the digits (#3839's shrink-to-fit only masked it). A second, FM-mode-gated copy also lived on the VFO flag. Consolidate to a SINGLE WFM surface in the DAX pan overlay menu, directly below the "IQ Ch" selector — the natural home, since WFM demodulates that pan's DAX IQ stream on the PC. WFM runs on raw IQ and is mode-independent, so the menu toggle is always reachable (the old flag button only appeared in FM modes). - RxApplet: drop the WFM button + the vestigial setWfmActive(); keep the mode-combo wfmActivated(false) teardown. - VfoWidget: remove the FM-gated WFM button, its setWfmActive() reflector, the wfmActivated signal, and the FM-mode gating that showed/tore it down. - SpectrumOverlayMenu: add the WFM toggle below IQ Ch; emit wfmToggleRequested(on, sliceId); add a self-gated setWfmActive() reflector. - MainWindow_Wiring: route the menu toggle to activateWFM/deactivateWFM. - reflectWfmButtons: drive the single DAX-menu toggle. WFM remains a per-slice singleton; this only changes where the toggle lives. Note: with WFM no longer FM-gated, leaving FM mode on the flag no longer auto-tears-down the demod (it is raw-IQ, mode-independent); an explicit RxApplet mode change still tears it down. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bug
The VFO frequency readout clipped its leading (most-significant) MHz digits in the RX Controls box (
RxApplet) and the VFO slice flag (VfoWidget), and rendered far too small. Reported in #3463 (Default Light, Windows/FLEX‑6400, "first number cut"); also visible on macOS default dark.Root cause — two issues
QLabelstyled by the theme tokensfont.size.freq/font.family.freq(DSEG7 Modern), poured into a fixed box with no elision. A string wider than the box clips on the left (the label is right-aligned). In RX Controls the frequency also shared its row with TX/mode/WFM, leaving it only ~124px.setFont()didn't stick. Driving the fitted size viaQWidget::setFont()silently did nothing: aQLabelwhose stylesheet namesfont-familymakes Qt recompute the font from the QSS on every re-polish and discardsetFont(), falling back to the ~13px default. So the digits rendered at ~13px regardless of the fitted size — the real cause of "the frequency is tiny."The fix
FreqLabelFit.h::fitFreqPixelSize()computes the largest non-clipping pixel size; it's applied through the stylesheet as a literalfont-size(which always wins over the cascade), with colour/family still resolved from theme tokens so it stays theme-reactive. The last size is cached to skip a re-polish on every tune step, and each freq box is a stable, font-independent width (RxApplet stackExpanding, VfoWidget fixed-width) so the fit can't oscillate.Proof — agent automation bridge (live on a FLEX‑8400M)
tune+grab, before vs after, both themes. Left = original (clipped + ~13px); right = redesign (hero ~34px, decluttered).RX Controls — default dark, 21.074 MHz

RX Controls — light theme, 21.074 MHz

VFO slice flag — unchanged (theme nominal, no clip)

2clipped, ~13px21.074.000@ ~34px1gone,4clipped144.200.000, no clip074.000shown21.074.000Files
src/gui/FreqLabelFit.h(new) —fitFreqPixelSize()src/gui/RxApplet.{h,cpp}— hero frequency row, context strip, literal-QSS sizingsrc/gui/VfoWidget.{h,cpp}— literal-QSS sizing + re-fit hooksRebased onto current
main(includes #3800 / #3782). Closes #3463. Refs #3515.💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat