Repository navigation
Theme the tree-view rows so Theme Editor tokens stay readable (#5934). Principle VIII. - #5938
aethersdr-agent[bot] wants to merge 1 commit into
Conversation
…. Principle VIII. `font.family.freq` was never missing from the Theme Editor's Object list -- it was invisible. `appStylesheetTemplate()` in `src/gui/Theme.h` gives item-view background rules to `QListWidget` only; there is no `QTreeView` / `QTreeWidget` rule anywhere in the template. The Theme Editor's Object list is a `QTreeWidget` with `setAlternatingRowColors(true)` (`src/gui/ThemeEditorDialog.cpp:248`), so its alternate rows fall back to the native palette's `AlternateBase` -- near-white on Windows 11 -- while the item text still inherits the dark theme's `color.text.primary` (`#c8d8e8`). Light grey on white: every other row is unreadable, and `font.family.freq` happened to land on one. The reporter's screenshot is what established this; the first-pass hypothesis (a sticky Inspect subset) was an assertion, not evidence. - `src/gui/Theme.h`: add `QTreeView, QTreeWidget` background / alternate-background / color / border rules alongside the existing `QListWidget` block, plus `::item:selected` and `QHeaderView::section`. This covers every themed tree view at once. The other tree-view dialogs (`RadioSetupDialog`, `SettingsBrowserDialog`, `NetworkDiagnosticsDialog`) already carry widget-level stylesheets, which win over an ancestor's, so they are unaffected; the unstyled cases were the Theme Editor list and `QFileDialog`'s internal views. - `src/gui/ThemeEditorDialog.cpp`: the "inherited" cells painted a hardcoded `QColor(0x60, 0x70, 0x80)` that ignores the active theme (and is worse on Light). Now resolves `color.text.secondary`, seeded in both bundled themes (`ThemeSeedGenerated.cpp:145`). Antares localized `src/gui/FrequencyEntryParser.cpp`; read and discarded -- it has no bearing on theme-token rendering. Verified: `clang++ -fsyntax-only` on `ThemeEditorDialog.cpp` (which also includes `Theme.h`) is clean. No cross-TU symbol motion -- `tm.color()` is already used in the same function, and the `Theme.h` change is string-literal only -- so no test target gains a new link dependency. Blast radius: risk_score=0.055, 1 high-risk affected (top: MainWindow::MainWindow). `appStylesheetTemplate` has 14 transitive callers at depth 3, all of them theme-application entry points (`applyAppTheme`, `MainWindow::applyDarkTheme`, floating-container/workspace window constructors). Regression surface is purely visual: any tree view without its own stylesheet now paints themed.
There was a problem hiding this comment.
Issue fit
Yes. #5934 opened as "font.family.freq is missing from the Theme Editor", and the thread (ve7atj's screenshot) redefined it: the token is there, the alternate rows are illegible. I re-derived the root cause independently rather than taking the body's word for it, and it holds: appStylesheetTemplate() had rules for QListWidget (Theme.h:128-133) but nothing for QTreeView/QTreeWidget, the Object list is a QTreeWidget with setAlternatingRowColors(true) (ThemeEditorDialog.cpp:248), and Qt only sets QPalette::AlternateBase from a stylesheet when alternate-background-color is present — otherwise the native palette wins while the text keeps color.text.primary. The fix reaches this dialog: applyAppTheme(this) at ThemeEditorDialog.cpp:162.
Scope
| File / group | What it changes | Claimed by the issue? | Verdict |
|---|---|---|---|
src/gui/Theme.h — QTreeView, QTreeWidget + ::item:selected |
The reported defect, app-wide for every tree in an applyAppTheme() subtree |
Yes | In scope |
src/gui/Theme.h — QHeaderView::section |
Themes all header views, tables included, not just tree headers | Partly — tree headers yes, table headers are a widening | In scope but undisclosed — nit 2 |
src/gui/ThemeEditorDialog.cpp:486 |
Replaces the hardcoded #607080 "inherited" foreground with color.text.secondary |
Named in the triage comment, not in the PR body | In scope, and a measurable contrast win (below) |
Nothing else in the diff. No settings keys, no capability surface, no CHANGELOG entry, no new tokens — all four tokens in the new block (color.background.0/1, color.text.primary, color.border.strong) are already used elsewhere in the same template, so there is no new resolution risk.
Blockers
None.
Nits (non-blocking)
-
Two sibling surfaces still carry the identical defect. I walked every
setAlternatingRowColors(true)site. TheQListWidgetones were already covered; five tables live in dialogs that style themselves. Two of those style the table but never setalternate-background-color, so their alternate rows still fall back to nativeAlternateBasewith light-grey text on Windows — exactly #5934:src/gui/RadioHealthDialog.cpp:58-71—setAlternatingRowColors(true)plus a themed sheet settingbackground+coloronly.src/gui/NetworkDiagnosticsDialog.cpp:808— alternating rows on; noalternate-background-coloranywhere in that file.
Not a reason to hold this PR (it fixes what was reported), but "fixes every themed tree at once" is true and "fixes this class of bug" is not. Worth a follow-up issue, or one line in each of those two sheets.
-
Principle VIII is cited but the body carries no evidence. The template's test plan is absent entirely — no platform note, no before/after. The defect is specifically the Windows 11 native palette, which the authoring agent cannot have exercised. CI green (all 7 checks on
422e6dc, including check-windows and Static checks) proves it compiles, not that the rows are readable. Cheapest closure: ask @ve7atj to confirm on the build they screenshotted, in both Dark and Light. -
Inline: stale "inherited" foreground if the operator edits
color.text.secondaryitself (ThemeEditorDialog.cpp:486). -
Inline:
QHeaderView::sectionreach (Theme.h:150).
What I tried to break
- Invalid brush from an unresolved token.
QBrush(tm.color("color.text.secondary"))would paint garbage if the key were missing. It isn't: seeded at root insrc/core/ThemeSeedGenerated.cpp:145(#8ea8c0) and present in both bundled themes ({color.gray.400}dark,{color.gray.100}light). Survives. - Is the "unreadable on light backgrounds" claim real, and does the new value regress Dark? I resolved the ramps and computed contrast both ways. Light (
bg.1 #dde5ed):#607080→ 4.0:1, new#506070→ 5.08:1. Dark (bg.1 #1a2a3a):#607080→ 2.87:1, new#8ea8c0→ 5.93:1. The old value was worse on Dark, not just Light — the comment slightly misattributes it, but the change is an improvement in both. Survives. - Per-widget sheets clobbered by the new app-level rules. Five files use trees.
SettingsBrowserDialog(kDialogStyle, lines 58-62),RadioSetupDialog(755-759) andNetworkDiagnosticsDialogall carry their own, closer, equal-or-greater-specificity tree rules, so they are unchanged.NetworkDiagnosticsDialog.cpp:362-366sets a per-itemQt::ForegroundRolefor category rows, which wins over a view-levelcolor:. Survives. - Branch/expand indicators on newly-stylesheeted trees. The Theme Editor list is flat (
addTopLevelItemonly, ThemeEditorDialog.cpp:418), so there is no branch column to lose; the hierarchical trees elsewhere already ship their own::branchrules (e.g. RadioSetupDialog.cpp:757). The one surface I could not reason to a conclusion is the non-nativeQFileDialoginImageFileDialog.cpp:263-273, which is parented and so inherits these rules — worth a glance from anyone with a build. - Hardcoded
#000on selection. Matches the existingQListWidget::item:selectedprecedent one line above, and black on accent (#00b4d8dark /#0088b0light) clears AA both ways. Not a finding.
All of the above is reasoned from the head checkout at /tmp/aetherclaude/pr-5938; I have no build and no GUI here, so nothing was reproduced at runtime.
Recommendation
Approve with nits. The root cause is correctly identified and independently confirmable from the code, the fix is minimal and sits in the canonical template where it belongs, and both colour changes measurably improve contrast in both bundled themes. Next step: have the reporter confirm the paint on Windows 11 in Dark and Light, and open a follow-up for the two tables in the first nit — no need to grow this PR.
🤖 aethersdr-agent · cost: $10.7536 · model: claude-opus-5
| background-color: {{color.accent}}; | ||
| color: #000; | ||
| } | ||
| QHeaderView::section { |
There was a problem hiding this comment.
QHeaderView::section reaches further than "tree-view rows": it matches table headers too, app-wide within every applyAppTheme() subtree. I checked the blast radius — 15 files already define their own QHeaderView::section and are unaffected (their rule is closer and equal-specificity). The ones that newly change appearance are KiwiPublicReceiverPicker, MqttSettingsDialog, NetSchedulerDialog, SystemInfoDialog and the non-native QFileDialog in ImageFileDialog.cpp.
That's most likely an improvement, and tree headers genuinely need it — but it is a user-visible restyle of surfaces the issue never mentions. Please say so in the PR body so the maintainer is ruling on it knowingly rather than discovering it post-merge.
| item->setForeground(col, QBrush(QColor(0x60, 0x70, 0x80))); | ||
| // Themed, not hardcoded: a fixed slate ignores the active | ||
| // theme and is unreadable on light backgrounds (#5934). | ||
| item->setForeground(col, QBrush(tm.color(QStringLiteral("color.text.secondary")))); |
There was a problem hiding this comment.
Good change — I measured it and it improves contrast in both themes (Dark 2.87:1 → 5.93:1, Light 4.0:1 → 5.08:1 against background.1), and color.text.secondary resolves from the compiled-in seed, so there's no invalid-QColor path.
One consequence the old constant didn't have: this value is now baked into the item at populateRow() time. onActiveThemeChanged() (line ~1116) only calls refreshTokenList() when the active theme name changes; an in-place token edit repopulates just the row that was clicked. So editing color.text.secondary itself — in this very dialog, which is the one place people do that — leaves every other row's "inherited" cells painted in the previous colour until a scope switch or theme reload.
Cosmetic and self-correcting, not worth holding the PR. If you want it exact, having onActiveThemeChanged() also refresh when the edited key is color.text.secondary would close it.
Ozy311
left a comment
There was a problem hiding this comment.
The PR fixes the reported alternating-row defect in #5934, but introduces a selected-row readability regression for custom themes with a dark accent. Request changes for the new selection rule; the alternating-row and inherited-text changes can stay.
Scope
| Change | Issue fit | Result |
|---|---|---|
Theme.h: tree background, alternate background, text and border |
Directly addresses the reporter's screenshot | Reproduced before/after; fixed |
Theme.h: selected-item styling |
Adjacent to the list fix | New dark-accent regression; inline finding below |
Theme.h: generic header sections |
Proposed in the issue thread, broader than the PR body's description | Reaches table headers too; existing discussion already covers this |
ThemeEditorDialog.cpp: inherited foreground token |
Proposed in the issue thread | Improves theme switching; existing live-edit refresh limitation remains |
Blocker
- P2 — Preserve readable selection with dark custom accents (
Theme.h:146–149). The new rule forces black selected text oncolor.accent. Withcolor.accent=#203040, the selected token name and value become almost invisible (nominal contrast 1.56:1). The same actual dialog on the merge base retains readable native selection. Removing only this new four-line selection block restores that selection while preserving the fixed alternating backgrounds. The inline suggestion applies that minimal correction.
Verification
Built the actual ThemeEditorDialog and its production theme/editor/settings dependencies in a standalone, socket-free harness on macOS, Qt 6.11.1, Fusion style, offscreen platform, isolated settings. Compared the two changed files from merge base 2859fdef with head 422e6dc5, and with a scratch variant deleting only the new selection block. This exercised the real dialog, not a reimplementation of the stylesheet.
- Default Dark alternate-row pixels: base #f7f7f7, head #1a2a3a; ordinary rows stay #0f0f1a. This reproduces the reported class of defect and its fix.
- Default Light rows on head: #f5f5f8 / #dde5ed. Dark → Light → Dark retained all 154 token rows, including
font.family.freq. - Dark custom accent: rendered the new dark-on-dark selected row, then demonstrated that deleting only the selected-item block restores readable native selection without losing the alternating-row fix.
- Confirmed the existing inherited-text refresh observation: editing
color.text.secondaryin place leaves existing inherited cells at their old brush; switching themes refreshes them.
This is a focused production-dialog reproduction, not native Windows verification or a full application/automation-bridge run. No radio connection or TX was involved. The seven checks succeeded for this head; Linux/macOS/Windows build steps actually ran. Those PR runs were created September 24 and do not validate the newer main tip. The full suite and native Windows visual confirmation were not run locally.
Existing non-blocking observations
I am not duplicating the active threads for header scope and inherited-cell live refresh. The PR body should also record the validation and broader header reach.
Snapshot: head 422e6dc54177cd7b7bf89b780bbd380d3d4f193e; merge base 2859fdef4c5121fe0d18f5d08f819ecbd9b33fc3; trusted current-main governance 33c0032c2b2d8ed9b74be3a5a80cf86c9930bbc2. Governance manifest PASS, required documents read, Constitution mirrors identical. Neither changed file nor the core theme/token resources changed between the merge base and current main. GitHub reports the commit signature verified / valid. Required human CODEOWNERS review remains separate.
— Ozy · Reviewed with Codex (GPT-6)
| QTreeView::item:selected, QTreeWidget::item:selected { | ||
| background-color: {{color.accent}}; | ||
| color: #000; | ||
| } |
There was a problem hiding this comment.
[P2] Keep selected rows readable when the theme accent is dark
With a custom theme whose color.accent is #203040, selecting a Theme Editor row now renders its name and value in black on that dark accent (1.56:1 nominal contrast). I reproduced this using the actual dialog at this head under Qt 6.11.1/Fusion/offscreen; the merge-base dialog kept readable native selection. The new literal also conflicts with the theme guide's “A contribution never introduces a new hardcoded colour literal.” The green CI checks do not exercise this rendering case. Removing this selection override preserves the alternating-row fix and restored readable selection in the same harness; alternatively introduce a proper selection-foreground semantic token with contrast validation. Minimal verified correction:
| QTreeView::item:selected, QTreeWidget::item:selected { | |
| background-color: {{color.accent}}; | |
| color: #000; | |
| } |
ten9876
left a comment
There was a problem hiding this comment.
The PR fixes the alternating-row readability defect clarified in #5934, but the added selected-item rule introduces another unreadable state for custom dark accents. Request changes on head 422e6dc54177cd7b7bf89b780bbd380d3d4f193e. I independently reproduced the earlier selection finding; the head has not changed since that review.
Scope
| File/group | What changes | Claimed/explained? | Verdict |
|---|---|---|---|
src/gui/Theme.h:140–145 |
Tree backgrounds, alternate backgrounds, text and border | Title/body and issue thread | Direct fix |
src/gui/Theme.h:146–149 |
Selected tree item colours | Issue thread | In scope, but introduces blocker 1 |
src/gui/Theme.h:150–155 |
Generic header backgrounds, text, border and padding | Issue thread; absent from terse PR body | Also reaches tables; disclose scope |
src/gui/ThemeEditorDialog.cpp:484–488 |
Inherited-cell foreground resolves a theme token | Issue thread | In scope; refresh limitation below |
Everything in the diff is explained by the issue thread. The single commit has no unrelated authored-date outlier. No settings keys, public APIs, dependencies, removed guards, or CHANGELOG changes. The body has no scope checklist to contradict, but underdescribes the header and inherited-cell changes. The original row correction is a bug fix, not a visual preference; the broader header presentation should be made explicit for Jeremy's design review.
Blockers
-
P2 — Preserve readable selected text with dark custom accents (
src/gui/Theme.h:146–149). The new rule forcescolor: #000against arbitrarycolor.accent. With accent#203040, selectingfont.family.freqin the actual production dialog renders black glyphs on that dark background (nominal contrast 1.56:1). The merge-base dialog retains readable native selection. Removing only this four-line rule restores that selection while keeping the alternating-row fix. This independently confirms the existing blocker rather than adding a new inline thread.Observed with Qt 6.11.2, Fusion, offscreen on Linux:
merge base: selected blackPixels=0 bg=#308dc8 PR head: selected blackPixels=21 bg=#203040 no-selection-rule mutation: blackPixels=0 bg=#308dc8Minimal correction: delete the new selection block. The resulting adjacency is:
QTreeView, QTreeWidget { background-color: {{color.background.0}}; alternate-background-color: {{color.background.1}}; color: {{color.text.primary}}; border: 1px solid {{color.border.strong}}; } QHeaderView::section { background-color: {{color.background.1}}; color: {{color.text.primary}}; border: none; padding: 3px 6px; }Alternatively, introduce a properly themeable, readable foreground/background pair with appropriate design review. Copying an existing hardcoded list rule does not justify extending it:
docs/style/theme-style-guide.mdsays, “A contribution never introduces a new hardcoded colour literal.” The blocker rests on the reproduced readability regression, not merely this convention.
Nits — non-blocking
- Live inherited-cell refresh (
src/gui/ThemeEditorDialog.cpp:486,:540–552): existing cells retain a resolved brush whencolor.text.secondaryis edited within the same theme. Actual dialog output:inherited before=#8ea8c0 after=#8ea8c0 token=#f12345 text=inherited. Refresh affected inherited cells when this token changes, without invalidating rows still in use. This improves on the old fixed brush but does not yet track live edits. - Document the wider header reach and validation (
src/gui/Theme.h:150–155): this selector also styles table headers; e.g.MemoryDialog.cpp:390supplies table rules but no header override. Record that scope and before/after evidence in the body. Native Windows reporter confirmation remains outstanding. - Shorten the historical comment (
src/gui/Theme.h:134–139): retain the nativeAlternateBaseconstraint and issue reference; move the report narrative to the PR body.
What I tried to break / verification
- Built a socket-free harness using the actual
ThemeEditorDialog, production theme/editor/settings dependencies and bundled resources. Compared merge base2859fdef4c5121fe0d18f5d08f819ecbd9b33fc3, PR head, and a variant deleting only the new selection rule. No synthetic firmware peer or socket-owning test was introduced or executed. - Original defect reproduced: Default Dark alternate background is
#f7f7f7on base,#1a2a3aon head, while text remains#c8d8e8. Default Light head alternates#f5f5f8/#dde5ed. Both themes retain all 154 rows andfont.family.freq. In this fixture other font rows occupy the unreadable alternate bands; the exact reporter's row ordering was not claimed reproduced. - Read the sibling styles: Radio Setup, Network Diagnostics and Settings Browser tree-specific rules retain precedence. These conclusions are source-reviewed, not a claim that every sibling GUI was driven. No new lifecycle, persistence, threading or engine-boundary change was found.
- The separate
code-reviewskill was unavailable; performed manual adversarial review plus an independent parallel scope audit. No PR tests were added, so there is no submitted regression test to mutation-check; the diagnostic selection-rule mutation above isolates causality.
Full AetherSDR Release build succeeded on Linux/Qt 6.11.2. Drove that exact PR-head binary through explicit socket /tmp/aethersdr-pr5938-review, offscreen, isolated settings/config and TX disabled. Connected explicitly to the simulator; never connected to the discovered live radio. Bridge evidence (relevant fields):
{"model":"AetherSDR Demo","serial":"DEMO-0001","connected":true,"transmitting":false}
{"action":"selectRow","class":"QTreeWidget","ok":true,"selectedRow":138,"selectedRowText":"font.family.freq","target":"ThemeEditorDialog/QTreeWidget"}
{"bytes":88401,"class":"ThemeEditorDialog","height":720,"ok":true,"path":"/tmp/pr5938-session/freq-selected.png","target":"ThemeEditorDialog","width":533}invoke opened Theme Editor, switched Default Dark → Default Light → the isolated custom dark-accent theme, and selected the reported token. grab reproduced the black-on-dark selection in the full app as well. Closing/reopening the dialog survived and produced another successful capture. Disconnected the demo and used the owned application's Quit action; the instance exited. The full app was driven on head only; before/after and mutation evidence came from the focused production-dialog harness. The operator's existing app was left untouched.
All seven recorded checks passed for this head, but the pull_request CI runs were created 2026-09-24 12:26:59 UTC, before current main 8df6db2f (2026-10-04 18:26:40 UTC). Branch protection reports strict: false; those checks do not validate today's merge result. GitHub reports the commit signature verified/valid. No full test suite or native Windows visual run was performed. Recommendation: fix selected-row contrast, record validation and scope, then rerun checks on the updated branch. Approval remains the operator's decision.
ten9876
left a comment
There was a problem hiding this comment.
Issue fit: yes, for the alternating-row defect that #5934's thread clarified (the token was never missing; every other row was light text on the native near-white AlternateBase). The head is unchanged (422e6dc5) since the two earlier change requests, and it merges cleanly onto current main (git merge-tree against 85f2a1249: no conflicts). Neither changed file has moved on main except #6072's comment pass. One blocker remains, and I'm adding a canon citation to it.
Scope
| File/group | Change | Claimed? | Verdict |
|---|---|---|---|
src/gui/Theme.h:140-145 |
Tree background, alternate background, text, border | Yes | The fix |
src/gui/Theme.h:146-149 |
QTreeView::item:selected → accent background with color: #000 |
In the issue thread only | Blocker 1 |
src/gui/Theme.h:150-155 |
QHeaderView::section: reaches every header, tables included |
In the issue thread; not in the body | Nit 2 |
src/gui/ThemeEditorDialog.cpp:484-486 |
"inherited" foreground QColor(0x60,0x70,0x80) → color.text.secondary |
In the issue thread | In scope, and an improvement |
One commit, authored 2026-09-24. There are no settings, public surface, test or CHANGELOG changes. Socket preflight: no test sources touched.
Blockers
-
src/gui/Theme.h:146-149adds a new hardcoded colour literal (color: #000).docs/style/theme-style-guide.mdstates the rule in its first sentence: "Every colour in AetherSDR resolves through a ThemeManager token. A contribution never introduces a new hardcoded colour literal." §3 adds: "No new hex/RGB literals insrc/UI code — not in QSS strings."CI is green only because the ratchet nets this to zero: the same PR deletes
QColor(0x60,0x70,0x80), soaudit_colours.py's total does not rise. That is a swap, not compliance.The runtime consequence is the one @Ozy311 and the 2026-10-04 review each reproduced independently. With a custom dark
color.accent(e.g.#203040), the selected row is black text on a dark background (about 1.56:1), where the merge base kept readable native selection. I did not re-reproduce that dark-accent case. I'm relying on two independent pixel reproductions plus the canon sentence.Minimal fix: delete the four-line
::item:selectedblock. The resulting adjacency is in the 2026-10-04 review and is drop-in.Context for the operator, not a reason to keep the block:
mainalready does the same thing inQListWidget::item:selected(Theme.h:90) andQMenu::item:selected(:167). A dark custom accent is therefore already unreadable in menus and lists app-wide. The durable fix is an on-accent text token used by all three selectors, which is a follow-up and not this PR's job. This PR shouldn't add a third instance.
Nits (non-blocking)
- Three unresolved inline threads block the merge under
main's required conversation resolution:Theme.h:149(Ozy311),Theme.h:150andThemeEditorDialog.cpp:486(aethersdr-agent). Theme.h:150-155:QHeaderView::sectionthemes every table header in the app, not just tree headers. That's probably right, but it isn't in the body. Please say so there, so the reach is searchable later.Theme.h:134-139: the comment narrates the report ("the Theme Editor's Object list looked like it was missing rows"). Keep the native-AlternateBaseconstraint and the issue number, and move the rest to the PR body (AGENTS.md §"Comments").- Two sibling tables still carry the same defect on current
main. Both setsetAlternatingRowColors(true)but have noalternate-background-color(0 hits in either file):RadioHealthDialog.cpp:58andNetworkDiagnosticsDialog.cpp:808. They're out of scope; a follow-up issue would cover them.
Verified empirically, and what I tried to break
- On current
main, not only on the September base.- Setup: I built
main85f2a1249plus this PR's diff (Release), and drove it offscreen with isolated settings, an explicit socket andAETHER_AUTOMATION_NO_TX=1. - Connected explicitly to
DEMO-0001;get radioreturned"model": "AetherSDR Demo"and"transmitting": false. invoke "Theme Editor…" triggerthengrab ThemeEditorDialog: in Default Dark, the Object list alternatesbackground.0/background.1with readabletext.primaryon both bands, which is the reporter's defect fixed.
- Setup: I built
- Did
mainalready fix it some other way, making this redundant? No.main'sTheme.hhas noQTreeView/QTreeWidget/QHeaderViewrule (onlyQListWidget,QMenuBarandQMenuselectors), andThemeEditorDialog.cpp:484still has the hardcoded slate. - Does the ratchet catch the literal? It doesn't (net zero, above), which is why blocker 1 cites the guide rather than CI.
- Not tested: native Windows 11 (the reporter's platform), and the dark-accent selection, which rests on the two prior reproductions.
- CI is from 2026-09-24, before
main's 2026-10-04 tip. The cleanmerge-treeis the current-base evidence. - Auto-merge: squash auto-merge has been armed by
aethersdr-agentsince 2026-09-24, so approving after the fix merges it immediately.
Recommendation: Request changes, for blocker 1 only: drop the selection block. Everything else is ready.
— Jeremy KK7GWY + Claude
|
@aethersdr: this PR is superseded by #6312, which fixes #5934 from my fork. #6312 keeps what the three reviews accepted here and resolves the blocker:
This PR's head hasn't changed since the change requests, so it can be closed in favour of #6312. That's the maintainers' call. |
…Editor's token list is readable (#5934). Principle VIII. (#6312) Fixes #5934. Supersedes #5938: it keeps that PR's fix and drops the rule its three reviews blocked. ## What The Theme Editor's Object list showed every other row as light grey text on a near-white background, so tokens such as `font.family.freq` looked missing (screenshot in [the issue thread](#5934 (comment))). The token was always there. ## Root cause `appStylesheetTemplate()` (`src/gui/Theme.h`) gives `QListWidget` a background and an `alternate-background-color`, but had no rule for tree views. Qt only takes `QPalette::AlternateBase` from a stylesheet when `alternate-background-color` is set, so a `QTreeView` with alternating rows kept the **native** AlternateBase. Under a light Windows palette that colour is near-white, while the text is the dark theme's light `color.text.primary`. The Object list is a `QTreeWidget` with `setAlternatingRowColors(true)`. It only shows when the OS palette is light. On Windows in dark mode the native AlternateBase is already dark, which is why it wasn't reproducible here until Qt was forced to the light palette. ## How - **`Theme.h`:** `QTreeView` (which covers `QTreeWidget`) gets `background.0`, `background.1` for alternate rows, `text.primary` and `border.strong`. Headers inside tree views (`QTreeView QHeaderView::section`) get `background.1`, `text.primary`, and right/bottom `border.strong` separators so the header stays distinct from the `background.1` alternate rows under it. - **Selection is left to the palette, for lists too.** #5938 added `QTreeView::item:selected { background: accent; color: #000 }`, which reviewers measured at 1.56:1 on a dark custom accent; it also added a hard-coded colour. Not included. The template's existing `QListWidget::item:selected` rule had the same black-on-accent shape and is removed, so **a selected row in any list that relies on the app stylesheet now draws the palette highlight** (white on blue in Default Dark) instead of black on the accent. Lists with their own selection rule (Radio Setup, Favorites picker, Profile Manager, Audio Device Change) are unchanged. - **Header scope:** the header rule is scoped to tree views, so table headers elsewhere don't change. #5938's unscoped `QHeaderView::section` reached five other dialogs. - **`ThemeEditorDialog.cpp`:** "inherited" cells use `color.text.secondary` instead of the fixed slate `#607080`. #5938's review measured that change at 5.93:1 in the dark theme and 5.08:1 in the light one. On an in-place token edit the dialog repaints those cells without rebuilding the list, so editing `color.text.secondary` itself updates them. A rebuild there is what the existing comment warns crashes; this path repaints only, finds the cells by an item data role rather than by their text, and runs only when `color.text.secondary` actually changed. - **`RadioHealthDialog.cpp`:** the register table alternated rows without an `alternate-background-color` — the same defect — and now uses `background.1`. - **Theme drops:** `ThemeEditorDialog::dropEvent` imported every local URL without the `.aethertheme` / `.json` check `dragEnterEvent` applies. Both now use `ThemeManager::isImportableThemeFile()`. The related stylesheet-injection finding from the security pass is tracked separately in #6320. **Blast radius of the tree rule:** it applies wherever `applyAppTheme()` reaches. `RadioSetupDialog` and `SettingsBrowserDialog` have their own, closer `QTreeWidget` rules and are unchanged. `NetworkDiagnosticsDialog`'s navigation tree has its own ID-selector rule and doesn't alternate rows, so it is effectively unchanged. `ImageFileDialog`'s non-native file dialog (background-image picker) in Detail view is a `QTreeView` under a themed ancestor, so it newly gets the themed background, text and border. ## Testing ### Proof with the agent automation bridge Runs used the fork's Windows Installer build of this branch (Qt 6.12, run 37878449329) and the same-day `main`-based build as "before". Both ran on Windows 10 22H2, in **isolated settings profiles** (`AETHER_SETTINGS_DIR`, no radio, Default Dark theme), with Qt forced to the light system palette (`QT_QPA_PLATFORM=windows:darkmode=0`), which is the reporter's condition. Steps: `invoke "Theme Editor" trigger`, `grab_widget QTreeWidget`, then `clickAt` a row and grab again. | | Light OS palette | Light OS palette, row selected | Dark OS palette, row selected | |---|---|---|---| | **Before** (main) | Every other row near-white, text unreadable | — | — | | **After** (this PR) | All rows readable, alternate rows `background.1` | Light-grey highlight, dark text (palette) | Dark-grey highlight, white text (palette) | <img width="624" height="742" alt="5934-proof-before-after" src="https://github.com/user-attachments/assets/bc318228-64b2-4451-b6f6-19cdfc4f7b62" /> ### Unit test (local, Qt 6.12.0 pinned, MSVC 2022, offscreen) `theme_manager_test` renders a `QTreeWidget` with alternating rows under the app stylesheet. It checks that rows 2 and 3 are exactly `background.0` and `background.1`, and that no list or tree `::item…:selected` rule is in the template. A second block renders a two-column header and checks the right and bottom separators are `border.strong`, and a third pins `isImportableThemeFile()` for accepted, rejected and non-local paths. (Rows 0 and 1 aren't used, because row 0 is the current item and carries the style's focus tint.) - As written: **passes**. - Mutation, `alternate-background-color` removed from the tree rule: **fails** (the alternate-row check). - Mutation, the black-on-accent selection rule put back: **fails**. - Mutation, the tree rule's `background-color` removed: still passes. The app-wide `QWidget { background-color }` rule already paints normal rows `background.0`, so that line is explicit rather than load-bearing. `theme_manager_test` isn't in the frozen per-PR test gate; it runs on pushes to `main`. ### Maintainer remediation pass (Linux, Qt 6.12, demo simulator) - `theme_manager_test` and `theme_seed_test` pass. Mutations: either header border removed → fails (`:1719`, `:1720`); the `QListWidget::item:selected` rule put back → fails (`:1692`); the import predicate forced to `true` → fails (`:1703`, `:1704`). - Bridge, demo only, isolated settings: the Theme Editor header shows its column separator; with the `applet/acom` scope selected the inherited cells are italic `text.secondary` and overridden cells keep the primary text colour; the connection panel's radio list (`connectionLocalRadioList`) shows a selected row as white text on the palette's blue highlight in Default Dark. - Not reached on the demo: Radio Health (the demo reports no health registers, so the table is empty), and the in-place inherited-cell repaint (the UI rebuilds the list when the scope changes, so no in-place edit of the root `text.secondary` leaves scope columns on screen). ### Not verified - **A dark custom accent, screenshotted.** User themes live in `%LOCALAPPDATA%\AetherSDR\themes`, which `AETHER_SETTINGS_DIR` doesn't isolate, so I didn't write a test theme into the operator's profile. Selection now comes from the palette, so the accent colour no longer affects the selected row's contrast; the unit test guards against the rule coming back. - **Windows 11 native**, the reporter's platform. Reproduced with Qt's light palette on Windows 10 instead. - **macOS and Linux**, where the stylesheet change applies too. The same `alternate-background-color` mechanism applies there. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: CJ Johnson <chibondking@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Jeremy [KK7GWY] <kk7gwy@aethersdr.com>
Pull request was closed
Summary
Fixes #5934
What was changed
Theme the tree-view rows so Theme Editor tokens stay readable (#5934). Principle VIII.
Files modified
src/gui/Theme.hsrc/gui/ThemeEditorDialog.cppGenerated by AetherClaude (automated agent for AetherSDR)
🤖 aethersdr-agent · cost: $3.9427 · model: claude-opus-5