Skip to content

cleanup(theme): tokenize hardcoded slider sites (Panadapter / Eq / RadioSetup / FlexControl)#3204

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
nigelfenton:feat/theme-slider-hardcoded-cleanup
May 27, 2026
Merged

cleanup(theme): tokenize hardcoded slider sites (Panadapter / Eq / RadioSetup / FlexControl)#3204
ten9876 merged 1 commit into
aethersdr:mainfrom
nigelfenton:feat/theme-slider-hardcoded-cleanup

Conversation

@nigelfenton
Copy link
Copy Markdown
Contributor

Closes the second half of Pat's "both" answer to (a) the toggle namespace work (landed as #3198) and (b) the hardcoded-hex slider sites called out in #3188's Out of scope section.

Background

#3188 tagged four sites as bypassing the color.slider.* namespace entirely:

Hardcoded-hex slider sites in PanadapterApplet, EqApplet, RadioSetupDialog, FlexControlDialog bypass the theme system entirely. Each needs its local stylesheet dropped in favour of the global QSS + per-applet override path.

Each site used inline setStyleSheet(...) calls full of hardcoded hex values — so the v2 per-applet override path couldn't reach them, and theme switches required an app restart. This PR tokenizes the colours and routes through ThemeManager::applyStyleSheet so:

  • the per-applet override cascade lights up automatically once a relevant scope exists (none of the four currently sits under applet/tx|rx|comp, so the immediate effect at root scope is the canonical Wave-blue / accent-success treatment);
  • live theme switching (Dark ↔ Light without restart) re-resolves the tokens and repaints, matching the rest of the namespace-aware UI.

Approach — Option A (tokenize colours, preserve sizes)

Site-local slider dimensions are preserved intentionally — the four sites each chose bespoke handle widths / groove heights for the UX context they live in (Panadapter's tiny 8px handle for the CW bar, EqApplet's 10×16 vertical handle for band-column rhythm, RadioSetup's emphasis-sized 14px handle, FlexControl's 16px bordered ring).

Homogenizing them via applyPrimarySliderStyle would have been a more literal interpretation of "drop the local stylesheet" but would have introduced visible regressions across all four sites. Option A (tokenize colours, preserve sizes) achieves Pat's stated goal — sliders honour the theme system — without the visual cost.

Per-site notes

File Sliders Token migration
PanadapterApplet.cpp 2 pitch sliders (CW decoder bar) #1a2a3a groove → {{color.slider.background}}
#6a8090 handle → {{color.slider.handle}}
EqApplet.cpp 8 vertical EQ band sliders #203040 groove → {{color.slider.background}}
#00b4d8 handle → {{color.slider.foreground}} (the EQ idiom puts the accent colour on the handle itself — no sub-page rule)
RadioSetupDialog.cpp 3 filter-sharpness sliders (voice / cw / digital) #1a2a3a groove → {{color.slider.background}}
#c8d8e8 handle → {{color.slider.handle}}
(lineoutSlider + hpSlider already inherit from the global QSS — no migration needed)
FlexControlDialog.cpp 2 sliders (spin / sensitivity) inside the dialog-wide kFlexControlStyle template #162437 groove → {{color.slider.background}}
#65d379 sub-page → {{color.accent.success}} (preserves FlexControl green identity)
#d8e2ef handle → {{color.slider.handle}}
#65d379 handle border → {{color.accent.success}}
Body widget switched to applyStyleSheet so the tokens resolve + live re-theme registers

Verification

Windows 11 (MSVC + Ninja + Qt 6.10.3) on top of aethersdr/AetherSDR main at 4ee99f78.

Site Default Dark Default Light
PanadapterApplet pitch sliders ✓ dark groove + light handle ✓ light groove + dark handle — live re-theme adapts cleanly
EqApplet band sliders ✓ dark groove + accent-cyan handle ✓ handle resolves to light-theme accent (#0088b0)
RadioSetupDialog filter sliders ✓ dark groove + light handle ✓ light groove + dark handle — live re-theme adapts cleanly
FlexControlDialog spin + sensitivity ✓ dark groove + green sub-page + bordered handle ✓ sub-page green preserved (the green is color.accent.success which is green in both bundled themes; gray shades adapt around it)

theme_manager_test: zero new failures. The 4 pre-existing importThemeFromFile-related failures on clean main remain (Windows-vs-CI env issue, not introduced here).

Observed during testing — out of scope for this PR

While verifying in Default Light, two broader migration debt items came to light that go beyond this slider sweep:

  • RadioSetupDialog light-theme contrastkLabelStyle (color: #c8d8e8), kValueStyle (color: #00c8ff), kGroupStyle (border #304050, title #8aa8c0), and kEditStyle (background #1a2a3a) all hardcode dark-theme colours. Labels render as low-contrast pale text against the light-theme #f5f5f8 background, and edit fields look misplaced. Migrating those constants needs the same {{token}} + applyStyleSheet pattern and touches dozens of call sites across the dialog — a separate follow-up PR.
  • FlexControlDialog whole-dialog migration debt — the dialog template hardcodes #0b1625 button backgrounds, #31455f borders, #65d379 FlexControl-green accents throughout. The slider rules now adapt to theme; the rest of the dialog stays dark in light theme. A separate "FlexControl light-theme migration" PR would close this; touching it here would have ballooned the slider-cleanup scope.

Both observations were flagged as candidates for the next cleanup sweep that follows this one.

Together with #3198

This PR completes Pat's "both" answer. With #3198 (toggle namespace carve-out) and this PR (slider site cleanup) merged, the four originally-cited problem sites from #3188's Out-of-scope section all become theme-system-aware and the toggle button class joins the slider + knob namespace pattern as a third carved-out widget type.

cc @ten9876 @jensenpat @chibondking

73 Nigel G0JKN

🤖 Generated with Claude Code

…dioSetup / FlexControl)

Closes the second half of Pat's "both" answer to (a) toggle namespace
work (landed as aethersdr#3198) and (b) the hardcoded-hex slider sites called
out in aethersdr#3188's Out-of-scope section.

The four sites bypassed the color.slider.* namespace entirely with
inline `setStyleSheet(...)` calls full of hardcoded hex values, so the
v2 per-applet override path couldn't reach them and theme switches
required an app restart.  Each migrates the colours to {{token}} form
and routes through ThemeManager::applyStyleSheet so:

  - the per-applet override cascade lights up automatically once a
    relevant scope exists (none of the four currently sits under
    applet/tx|rx|comp, so the immediate effect at root scope is the
    canonical Wave-blue / accent-success treatment);
  - live theme switching (Dark ↔ Light without restart) re-resolves
    the tokens and repaints, matching the rest of the namespace-aware
    UI.

Site-local slider DIMENSIONS are preserved intentionally — the four
sites each chose bespoke handle widths / groove heights for the UX
context they live in (Panadapter's tiny 8px handle for the CW bar,
EqApplet's 10x16 vertical handle for band-column rhythm, RadioSetup's
emphasis-sized 14px handle, FlexControl's 16px bordered ring).
Homogenizing them via applyPrimarySliderStyle would have been a more
literal interpretation of "drop the local stylesheet" but would have
introduced visible regressions across all four sites.  Option A
(tokenize colours, preserve sizes) achieves Pat's stated goal —
sliders honour the theme system — without the visual cost.

Per-site notes:

  - **PanadapterApplet**: pitch min/max sliders in the CW decoder bar.
    Groove → color.slider.background, handle → color.slider.handle
    (replaces hardcoded #1a2a3a + #6a8090).  Now functional in light
    theme (was previously stuck rendering dark-theme colours).

  - **EqApplet**: vertical band sliders, all 8 frequency rows.  Groove
    → color.slider.background, handle → color.slider.foreground (the
    EQ visual idiom puts the accent colour on the handle itself —
    there is no sub-page rule, so foreground is the right token).
    Live theme switching adapts the accent shade between Default Dark
    (#00b4d8) and Default Light (#0088b0).

  - **RadioSetupDialog**: kFilterSlider used by the voice/cw/digital
    filter-sharpness sliders.  Groove → color.slider.background,
    handle → color.slider.handle.  Two other sliders in the dialog
    (lineoutSlider, hpSlider) already inherit from the global QSS
    set up in aethersdr#3188 — no migration needed there.

  - **FlexControlDialog**: spin + sensitivity sliders inside the
    dialog-wide kFlexControlStyle stylesheet.  Sliders kept the
    FlexControl green identity by routing the sub-page fill +
    handle border to color.accent.success (very close to the prior
    hardcoded #65d379 — resolves to #4dd87a in dark, #1a8040 in
    light).  Body widget now uses applyStyleSheet so the slider
    tokens resolve and the dialog body is registered for live
    re-theme.

Out of scope — observed during testing, filed as follow-ups:

  - **RadioSetupDialog has broader light-theme migration debt** —
    kLabelStyle / kValueStyle / kGroupStyle / kEditStyle constants
    still hardcode dark-theme colours (#c8d8e8 labels, #00c8ff
    values, #304050 borders, #1a2a3a edit fields).  Labels render
    as low-contrast pale text against the light-theme background.
    Beyond this slider sweep.

  - **FlexControlDialog has the same broader debt** — the dialog
    template hardcodes #0b1625 button backgrounds, #31455f borders,
    #65d379 FlexControl-green accents throughout.  The slider rules
    now adapt to theme; the rest of the dialog stays dark in light
    theme.  Beyond this slider sweep.

Verification on Windows 11 (MSVC + Ninja + Qt 6.10.3) on top of
aethersdr/AetherSDR main at 4ee99f7:

  - All 4 sites visually verified in Default Dark.
  - Live theme switch to Default Light — Panadapter pitch sliders +
    RadioSetup filter sliders adapt cleanly without restart (groove
    + handle gray shades flip).  EqApplet bands stay accent-coloured
    in both themes (intentional — handle uses color.slider.foreground
    which resolves to the active theme's accent blue).  FlexControl
    sliders' green sub-page stays green in both themes (the
    color.accent.success token IS green in both bundled themes).
  - theme_manager_test: zero new failures.  The 4 pre-existing
    importThemeFromFile-related failures present on clean main
    remain (Windows-vs-CI env issue, not introduced here).

cc @ten9876 @jensenpat @chibondking

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nigelfenton nigelfenton requested a review from a team as a code owner May 27, 2026 03:58
Copy link
Copy Markdown
Contributor

@aethersdr-agent aethersdr-agent Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Nigel — this is a clean, well-scoped follow-on to #3188 / #3198. Reviewed against AetherSDR conventions and the slider/knob namespace work that landed in #3188.

What works well

  • Scope matches the title exactly — four call sites, four files, no incidental drive-bys. The intentional decision to preserve site-local sizes (handle widths / groove heights) and tokenize colours only is the right call; the per-site dimensions carry real UX intent.
  • All four tokens used (color.slider.background, color.slider.handle, color.slider.foreground, color.accent.success) resolve in both bundled themes — verified against resources/themes/default-dark.json (lines 160-166 for slider namespace, line 44 for accent.success).
  • Switching bodyWidget()->setStyleSheet(...) to ThemeManager::applyStyleSheet(...) in FlexControlDialog.cpp:990-997 is the right shape — registers the body widget for live re-theme without changing its lifetime semantics.
  • The choice to route FlexControl's green identity through color.accent.success rather than introducing a new color.toggle.success.* token here keeps the slider sweep tightly scoped while still letting a future FlexControl-tribe carve-out retint cleanly.
  • Honest scope discipline on the call-outs in "Observed during testing" — flagging RadioSetupDialog label/value/group/edit hardcoded colours and the broader FlexControl dialog migration debt as separate follow-ups rather than scope-creeping this PR is the right call.

Conventions check

  • RAII / Qt parent ownership: untouched, all slider widgets retain their existing parenting through layout addition.
  • No QSettings / AppSettings interaction in scope.
  • No new error paths at system boundaries — applyStyleSheet is the established path and handles missing tokens at the resolver level.
  • No null deref risk; bodyWidget() is non-null by the time FlexControlDialog constructor reaches line 990.

One observation, not a request

The EqApplet handle uses {{color.slider.foreground}} rather than {{color.slider.handle}} — your PR description calls this out explicitly ("the EQ slider's visual idiom puts the accent colour on the handle itself"), and the in-file comment (EqApplet.cpp:79-86) documents the why. Worth noting that if EqApplet ever lands under applet/tx / applet/rx / applet/comp scope, the handle would retint to red/green/amber via the existing scope override — that's likely the intended cascade behaviour and matches the rest of the namespace, just flagging that it's a different cascade target than the other three sites in this PR.

Approving the design direction; no blockers from my read. 🟢


🤖 aethersdr-agent · cost: $9.4588 · model: claude-opus-4-7

@ten9876 ten9876 merged commit e4e0c11 into aethersdr:main May 27, 2026
5 checks passed
@nigelfenton nigelfenton deleted the feat/theme-slider-hardcoded-cleanup branch May 27, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants