Skip to content

refactor(gui): consolidate 8 divergent primary-slider styles into one Theme.h helper#3118

Merged
ten9876 merged 2 commits into
mainfrom
auto/slider-consolidation
May 25, 2026
Merged

refactor(gui): consolidate 8 divergent primary-slider styles into one Theme.h helper#3118
ten9876 merged 2 commits into
mainfrom
auto/slider-consolidation

Conversation

@ten9876

@ten9876 ten9876 commented May 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Pre-consolidation the codebase had eight near-duplicate `QSlider` stylesheet constants scattered across the applet family. Same slider widget, different look in three adjacent applets:

Style Files Distinctive properties
A — Cyan handle TxApplet, PhoneApplet, PhoneCwApplet, RxApplet groove `#203040`, handle 10×10 `#00b4d8`, no fill
B — Sub-page + bordered WaveApplet sub-page cyan, handle 12×12 light + cyan border
C — Hover-cyan AetherDspWidget, DspParamPopup groove `#304050`, handle 12×12 light, hover→cyan
D — Plain light VfoWidget groove `#1a2a3a`, handle 12×12 light, no hover
E — Themed cyan PanadapterApplet (CW sens) already token-based but cyan-handle look
F — Themed amber StripFinalOutputPanel, StripWaveformPanel sub-page amber for TX state
G — Tiny gray PanadapterApplet (Lo/Hi range) tiny gray handle — kept as deliberate de-emphasis
H — Plain light variant SpectrumOverlayMenu yet another local variant

The canonical look (per Jeremy's design call)

Sub-page fill from Wave-style value indicator + Plain light handle from Vfo-style.

```
groove: 4px, {{color.background.1}}, radius 2px
sub-page: {{color.accent}} (or caller-supplied token), radius 2px
handle: 12×12, {{color.text.primary}}, radius 6px — no border, no hover
```

API

Two helpers added to Theme.h:

```cpp
namespace Theme {
QString primarySliderStyleTemplate(const QString& accentToken = "color.accent");
void applyPrimarySliderStyle(QWidget* slider,
const QString& accentToken = "color.accent");
}
```

`applyPrimarySliderStyle()` routes through `ThemeManager::applyStyleSheet()` so each slider gets:

  • Token-resolved colours
  • Free live re-theme on theme change via the reverse-map
  • Both horizontal and vertical orientation rules

Variant tokens — the two transmit-adjacent Strip panels pass `color.accent.warning` for the amber sub-page that signals TX state. Same shape, different colour token; no per-call-site stylesheet needed.

Files migrated (13)

  • `Theme.h` — new helpers + updated default `QSlider` rules in `appStylesheetTemplate()`
  • `TxApplet` / `PhoneApplet` / `PhoneCwApplet` / `RxApplet` — Style A → canonical
  • `WaveApplet` — Style B → canonical (handle simplified)
  • `AetherDspWidget` / `DspParamPopup` — Style C → canonical (hover state dropped)
  • `VfoWidget` — Style D → canonical (now also has the sub-page indicator)
  • `PanadapterApplet` (CW sens slider only) — Style E → canonical
  • `SpectrumOverlayMenu` — both local Style H variants → canonical
  • `StripFinalOutputPanel` / `StripWaveformPanel` — Style F → canonical with `color.accent.warning` amber

Intentionally untouched (2)

Net code reduction

Lines added 108
Lines removed 134
Net -26 lines
kSliderStyle constants deleted 8
QSlider rules extracted out of dialog/widget mega-stylesheets 3

Build verified clean

  • AetherSDR builds clean (615 ninja steps, no errors)
  • All 320 targets including every test target

Phase 4 readiness

When the default-light theme ships, slider colours adapt automatically — every site reads through the same three tokens (`color.background.1`, `color.accent`/`color.accent.warning`, `color.text.primary`). No follow-up sweep needed.

Test plan

  • CI: build / check-paths / check-windows / CodeQL
  • Visual smoke: confirm the TX Controls, Wave, RX, Phone, CW, Vfo, AetherDSP, Quindar editor, and Test Tone dialog all render the same canonical slider; confirm StripFinalOutput + StripWaveform sliders render with amber sub-page fill (TX-state visual)

73, Jeremy KK7GWY & Claude (AI dev partner)

🤖 Generated with Claude Code

… Theme.h helper

Pre-consolidation the codebase had eight near-duplicate QSlider stylesheet
constants across the applet family, each with subtle differences:

  TxApplet / PhoneApplet / PhoneCwApplet / RxApplet  cyan handle, no fill
  WaveApplet                                         sub-page + bordered handle
  AetherDspWidget / DspParamPopup                    hover-cyan, distinct groove
  VfoWidget                                          plain light, third groove bg
  SpectrumOverlayMenu                                two more local variants
  StripFinalOutputPanel / StripWaveformPanel         themed amber sub-page
  PanadapterApplet (CW sens)                         partially themed cyan

Operator-visible drift: same slider widget, different look in three
adjacent applets. Maintainability drift: a Phase 4 light-theme tweak to
"slider handle colour" would have to touch nine separate stylesheet
literals.

This commit picks the canonical look:

  Sub-page fill (Wave-style value indicator) +
  Plain light handle (Vfo-style, no border, no hover)

…and exposes it through one helper:

  Theme::primarySliderStyleTemplate(accentToken = "color.accent")
  Theme::applyPrimarySliderStyle(slider, accentToken = "color.accent")

applyPrimarySliderStyle() routes through ThemeManager::applyStyleSheet()
so the slider gets:
  * Token-resolved {{color.background.1}} groove
  * {{color.accent}} (or caller-supplied token) sub-page fill
  * {{color.text.primary}} handle
  * Free live re-theme on theme change via the reverse-map
  * Both horizontal and vertical orientation rules

Variant tokens — the two transmit-adjacent Strip panels pass
"color.accent.warning" for the amber sub-page that signals TX state.
Same shape, different colour token; no per-call-site stylesheet needed.

## Files migrated (13)

  Theme.h                       + helpers, updated default QSlider
                                  rules in appStylesheetTemplate()
  TxApplet / PhoneApplet /      collapse kSliderStyle const,
   PhoneCwApplet / RxApplet     swap setStyleSheet → applyPrimarySliderStyle
   WaveApplet / DspParamPopup /
   VfoWidget / AetherDspWidget /
   SpectrumOverlayMenu /
   PanadapterApplet (CW sens)
  StripFinalOutputPanel /       same migration; amber variant via
   StripWaveformPanel           accentToken parameter

Two slider sites left intentionally untouched:
  * PanadapterApplet Lo/Hi range sliders — deliberate visual
    de-emphasis for secondary range controls; tiny gray handle is
    a load-bearing visual cue, not drift.
  * MeterSlider — already its own widget for VU/level meters with
    its own canonical style (handled in #3106).

## Build verified clean

  AetherSDR (615 ninja steps) + all 320 targets including every test.

73, Jeremy KK7GWY & Claude (AI dev partner)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 requested a review from a team as a code owner May 25, 2026 06:45
…der borders

Two follow-ups to #3118 from Jeremy's smoke test:

1. **Pan / balance sliders now fill from the centre outward.**  The L↔R
   pan in RxApplet and the L↔R balance in VfoWidget both use
   CenterMarkSlider, whose neutral position is the midpoint of the
   range.  The default stylesheet sub-page rule paints (0 → handle),
   which reads visually wrong on a centre-anchored control — a value
   slightly left of centre lit up the whole left half of the groove.

   CenterMarkSlider::paintEvent now over-paints the unwanted half of
   the sub-page with the groove colour (effectively erasing it) and
   adds the (centre → handle) accent fill when the handle sits left of
   centre.  Clipping excludes the handle pixel disc so the overpaint
   never bleeds into it.

   The fill resolves through ThemeManager so it tracks theme switches
   for free, same as every other primary slider in the codebase.

2. **All remaining `QSlider` border styling is now explicitly suppressed.**
   The pre-consolidation SpectrumOverlayMenu had `QSlider { border: none; }`
   in its local kSliderStyle to kill the platform-style 1-px border that
   shows up on some themes.  Dropping that line during the #3118 sweep
   re-introduced visible borders on the FFT AVG / FPS / Line / Fill /
   Floor / NB Blank / Black Level / Wtrfall Gain / Wtrfall Rate /
   BG Opacity sliders in the Spectrum Overlay Display tab.

   Theme::primarySliderStyleTemplate() and the QSlider section of
   appStylesheetTemplate() both now include `border: none;` on the
   QSlider element itself + on every subcontrol (groove / sub-page /
   add-page / handle) so the rule wins regardless of which platform
   style is providing the default.

The CenterMarkSlider class is currently duplicated between RxApplet.cpp
and VfoWidget.cpp; both copies got the identical fix. Consolidating
into a single shared header is reasonable cleanup but orthogonal to the
fix — keeping it for a follow-up.

73, Jeremy KK7GWY & Claude (AI dev partner)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 merged commit b05cd8a into main May 25, 2026
5 checks passed
@ten9876 ten9876 deleted the auto/slider-consolidation branch May 25, 2026 07:27
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
… Theme.h helper (aethersdr#3118)

## Summary

Pre-consolidation the codebase had **eight near-duplicate \`QSlider\`
stylesheet constants** scattered across the applet family. Same slider
widget, different look in three adjacent applets:

| Style | Files | Distinctive properties |
|---|---|---|
| A — Cyan handle | TxApplet, PhoneApplet, PhoneCwApplet, RxApplet |
groove \`#203040\`, handle 10×10 \`#00b4d8\`, no fill |
| B — Sub-page + bordered | WaveApplet | sub-page cyan, handle 12×12
light + cyan border |
| C — Hover-cyan | AetherDspWidget, DspParamPopup | groove \`#304050\`,
handle 12×12 light, hover→cyan |
| D — Plain light | VfoWidget | groove \`#1a2a3a\`, handle 12×12 light,
no hover |
| E — Themed cyan | PanadapterApplet (CW sens) | already token-based but
cyan-handle look |
| F — Themed amber | StripFinalOutputPanel, StripWaveformPanel |
sub-page amber for TX state |
| G — Tiny gray | PanadapterApplet (Lo/Hi range) | tiny gray handle —
**kept** as deliberate de-emphasis |
| H — Plain light variant | SpectrumOverlayMenu | yet another local
variant |

## The canonical look (per Jeremy's design call)

> Sub-page fill from Wave-style value indicator + Plain light handle
from Vfo-style.

\`\`\`
groove:    4px, {{color.background.1}}, radius 2px
sub-page:  {{color.accent}} (or caller-supplied token), radius 2px
handle: 12×12, {{color.text.primary}}, radius 6px — no border, no hover
\`\`\`

## API

Two helpers added to [Theme.h](src/gui/Theme.h):

\`\`\`cpp
namespace Theme {
QString primarySliderStyleTemplate(const QString& accentToken =
\"color.accent\");
    void    applyPrimarySliderStyle(QWidget* slider,
const QString& accentToken = \"color.accent\");
}
\`\`\`

\`applyPrimarySliderStyle()\` routes through
\`ThemeManager::applyStyleSheet()\` so each slider gets:
- Token-resolved colours
- **Free live re-theme** on theme change via the reverse-map
- Both horizontal and vertical orientation rules

Variant tokens — the two transmit-adjacent Strip panels pass
\`color.accent.warning\` for the amber sub-page that signals TX state.
Same shape, different colour token; no per-call-site stylesheet needed.

## Files migrated (13)

- \`Theme.h\` — new helpers + updated default \`QSlider\` rules in
\`appStylesheetTemplate()\`
- \`TxApplet\` / \`PhoneApplet\` / \`PhoneCwApplet\` / \`RxApplet\` —
Style A → canonical
- \`WaveApplet\` — Style B → canonical (handle simplified)
- \`AetherDspWidget\` / \`DspParamPopup\` — Style C → canonical (hover
state dropped)
- \`VfoWidget\` — Style D → canonical (now also has the sub-page
indicator)
- \`PanadapterApplet\` (CW sens slider only) — Style E → canonical
- \`SpectrumOverlayMenu\` — both local Style H variants → canonical
- \`StripFinalOutputPanel\` / \`StripWaveformPanel\` — Style F →
canonical with \`color.accent.warning\` amber

## Intentionally untouched (2)

- **\`PanadapterApplet\` Lo/Hi range sliders** — Style G is a deliberate
visual de-emphasis for secondary range controls; the tiny gray handle is
a load-bearing visual cue, not drift.
- **\`MeterSlider\`** — already its own widget for VU/level meters with
its own canonical style (aethersdr#3106).

## Net code reduction

| | |
|---|---|
| Lines added | 108 |
| Lines removed | 134 |
| Net | **-26 lines** |
| kSliderStyle constants deleted | 8 |
| QSlider rules extracted out of dialog/widget mega-stylesheets | 3 |

## Build verified clean

- [x] AetherSDR builds clean (615 ninja steps, no errors)
- [x] All 320 targets including every test target

## Phase 4 readiness

When the default-light theme ships, slider colours adapt automatically —
every site reads through the same three tokens (\`color.background.1\`,
\`color.accent\`/\`color.accent.warning\`, \`color.text.primary\`). No
follow-up sweep needed.

## Test plan

- [ ] CI: build / check-paths / check-windows / CodeQL
- [ ] Visual smoke: confirm the TX Controls, Wave, RX, Phone, CW, Vfo,
AetherDSP, Quindar editor, and Test Tone dialog all render the same
canonical slider; confirm StripFinalOutput + StripWaveform sliders
render with amber sub-page fill (TX-state visual)

73, Jeremy KK7GWY & Claude (AI dev partner)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant