Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat(amp): change fan-mode cycle button to a pull-down (#3905) - #4459

#4459
Open
M7HNF-Ian wants to merge 2 commits into
aethersdr:mainaethersdr/AetherSDR:mainfrom
M7HNF-Ian:fix/3905-amp-fan-pulldownM7HNF-Ian/AetherSDR:fix/3905-amp-fan-pulldownCopy head branch name to clipboard
Open

feat(amp): change fan-mode cycle button to a pull-down (#3905)#4459
M7HNF-Ian wants to merge 2 commits into
aethersdr:mainaethersdr/AetherSDR:mainfrom
M7HNF-Ian:fix/3905-amp-fan-pulldownM7HNF-Ian/AetherSDR:fix/3905-amp-fan-pulldownCopy head branch name to clipboard

Conversation

@M7HNF-Ian

Copy link
Copy Markdown
Contributor

Closes #3905.

What

The three fan modes (STANDARD/CONTEST/BROADCAST) were hidden behind a
click-to-cycle button — no way to see the choices without clicking
blind through them.

Swaps QPushButton m_fanBtn for a QComboBox with all three modes as
items (display text via the existing fanModeLabel(), uppercase mode
as itemData so the fanModeChanged contract — "uppercase, ready for
sendCommand" — is unchanged). setFanMode() now finds and selects the
matching item under QSignalBlocker when reflecting an incoming PGXL
status, so it doesn't echo a redundant setup fanmode= command back
to the amp.

No protocol change — MainWindow_Wiring.cpp's incoming-status and
outgoing-command wiring is untouched.

Test plan

No live PGXL amp available to test end-to-end (the AMP applet is
hardware-gated out of the dock without one), so extended
tests/amp_applet_test.cpp instead:

  • Fan combo exists with exactly 3 items
  • setFanMode() selects the matching item and shows the combo
  • setFanMode() does not emit fanModeChanged (the
    echo-prevention case — this is the main risk the triage flagged)
  • A user-driven selection does emit fanModeChanged with the
    correct uppercase mode
  • An unrecognized mode string leaves the selection unchanged
    instead of desyncing
  • All 13 pre-existing temperature-toggle tests still pass
    (unaffected, confirms no regression)

Fixes aethersdr#3905. The three fan modes (STANDARD/CONTEST/BROADCAST) were
hidden behind a click-to-cycle button — no way to see the choices
without clicking blind through them.

Swap QPushButton m_fanBtn for a QComboBox with all three modes as
items (display text via the existing fanModeLabel(), uppercase mode
as itemData so the fanModeChanged contract is unchanged). setFanMode()
now finds and selects the matching item under QSignalBlocker when
reflecting an incoming PGXL status, so it doesn't echo a redundant
"setup fanmode=" command back to the amp — the same failure mode the
old code guarded against with its own state tracking.

No live PGXL amp available to test end-to-end, so extended
tests/amp_applet_test.cpp instead: constructs a real AmpApplet
headlessly and asserts setFanMode() selects the right item, shows
the combo, and does NOT emit fanModeChanged (the echo-prevention
case); a user selection does emit it with the correct uppercase
value; and an unrecognized mode string leaves the selection
unchanged rather than desyncing. All pass, plus the 13 pre-existing
temperature-toggle tests (unaffected, confirms no regression).
@M7HNF-Ian
M7HNF-Ian requested review from a team as code owners July 25, 2026 15:38

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice, focused change — swapping the blind click-to-cycle button for a QComboBox is a clear UX win, and the implementation is tidy: itemData preserves the uppercase fanModeChanged contract so the protocol wiring is genuinely untouched, and the QSignalBlocker in setFanMode() is exactly the right way to reflect incoming PGXL status without echoing a command back. The connect is placed after the addItem loop so construction doesn't spuriously emit. Test coverage is thorough and pins the echo-prevention case that the triage flagged as the main risk. Conventions look good (ThemeManager, RAII, no QSettings).

Just two non-blocking notes below — nothing that needs to hold up the merge.

Polish (non-blocking)

  • Accidental mouse-wheel scroll over the combo will change fan mode and send setup fanmode= to the amp — worth suppressing for a hardware control.
  • Unknown-mode branch in setFanMode() leaves m_fanMode/accessibleName set to the unrecognized string while the combo keeps its prior selection — a small state desync.

The three CodeGuard CG-PATH-001 findings are all in .github/workflows/*.yml, which this PR doesn't touch — false positives, ignore them.


🤖 aethersdr-agent · cost: $5.0931 · model: claude-opus-4-8

Comment thread src/gui/AmpApplet.cpp
for (const QString& mode : {QStringLiteral("STANDARD"), QStringLiteral("CONTEST"), QStringLiteral("BROADCAST")})
m_fanCombo->addItem(fanModeLabel(mode), mode);
m_fanCombo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_fanCombo->setFocusPolicy(Qt::TabFocus);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking UX foot-gun: a QComboBox changes its selection on mouse-wheel scroll even when the popup is closed. Since this control commands the amplifier's fan, an operator scrolling the AMP applet (or the panel it sits in) can silently flip the fan mode and fire a setup fanmode= command. Consider ignoring wheel events unless the popup is open — e.g. install an event filter that eats QEvent::Wheel on the combo, or a tiny QComboBox subclass overriding wheelEvent() to no-op. Qt::TabFocus alone doesn't prevent this.

Comment thread src/gui/AmpApplet.cpp
Comment on lines +382 to +392
if (idx < 0) {
qWarning() << "AmpApplet: unknown fanmode" << m_fanMode;
} else {
// Reflecting an incoming PGXL status — block signals so this
// doesn't fire currentIndexChanged and echo a redundant
// "setup fanmode=" command back to the amp (#3905).
QSignalBlocker blocker(m_fanCombo);
m_fanCombo->setCurrentIndex(idx);
}
m_fanCombo->setAccessibleName(QString("Fan speed: %1").arg(m_fanMode));
m_fanCombo->show();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

On the unknown-mode branch, the combo's selection is (correctly) left untouched, but m_fanMode is still assigned the unrecognized value at line 380 and line 391 announces it via accessibleName — so after a bogus status the widget's internal mode and the a11y name disagree with what the combo actually shows. Minor, but you could tuck the setAccessibleName/show() inside the else, or skip overwriting m_fanMode when findData misses, to keep them consistent. The test already asserts the selection stays put, so this is purely about the stale m_fanMode/name.

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Cisco CodeGuard — static analysis of this PR (3 finding(s))

  • [MEDIUM] CG-PATH-001 — Potential path traversal in .github/workflows/macos-dmg.yml /tmp/aetherclaude/pr-4459/.github/workflows/macos-dmg.yml:82
  • [MEDIUM] CG-PATH-001 — Potential path traversal in .github/workflows/streamdeck-plugins.yml /tmp/aetherclaude/pr-4459/.github/workflows/streamdeck-plugins.yml:20
  • [MEDIUM] CG-PATH-001 — Potential path traversal in .github/workflows/streamdeck-plugins.yml /tmp/aetherclaude/pr-4459/.github/workflows/streamdeck-plugins.yml:25

Automated static scan by Cisco DefenseClaw CodeGuard on the changed files. Advisory — some may be false positives; the review above verifies them.


🤖 aethersdr-agent · cost: $5.2299 · model: claude-opus-4-8

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Antares Detector — candidate vulnerable file(s)

  • src/gui/AmpApplet.cpp
  • src/gui/MainWindow_Wiring.cpp

Localized by Cisco Foundation AI Antares-1B running locally in the AetherClaude sandbox, seeded by the Cartographer security map. Advisory only — please verify before acting.


🤖 aethersdr-agent · cost: $5.2299 · model: claude-opus-4-8

Address two review notes on aethersdr#4459:

1. m_fanCombo was a plain QComboBox, so hovering the mouse and
   scrolling would silently change fan mode and send a live "setup
   fanmode=" command to the amp. Swap to GuardedComboBox (already used
   for this exact reason elsewhere, e.g. AntennaGeniusApplet's device
   selector) — it only responds to wheel input while its dropdown is
   actually open.

2. setFanMode() unconditionally set m_fanMode to the incoming string
   even when it didn't match any combo item, leaving m_fanMode and the
   combo's displayed selection out of sync for an unrecognized mode.
   Now m_fanMode only updates in the branch that also updates the
   combo, so the two can't diverge.

Verified: extended amp_applet_test.cpp already covers setFanMode's
unknown-mode path (asserts the combo's selection is unchanged) — still
passes, plus all 21 other assertions, confirming no regression. Wheel
suppression itself isn't independently testable here (no live PGXL
amp available, and GuardedComboBox is an established, already-used
class) — relying on code reuse rather than a fresh test.
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.

Minor enhancement: Change Amplifier Fan button to pull-down

1 participant

Morty Proxy This is a proxified and sanitized view of the page, visit original site.