fix(gui): Profile Switcher non-editable combo focus swallows first Space PTT / CW key (#3908)#3926
Conversation
…ethersdr#3908) After selecting a profile from the Profile Switcher (or any other non-editable QComboBox), the combo retains keyboard focus. The first Space press then opens the combo's dropdown instead of keying PTT, because textInputCaptured() treats any focused QComboBox as a text input and handlePttHoldShortcut()/handleCwMomentaryShortcut() bail out rather than consuming the key. A second Space closes the popup and the next press finally keys TX. Add textEntryCaptured(), a narrower predicate that only counts editable text-entry widgets (QLineEdit, QTextEdit, QPlainTextEdit, QSpinBox, and editable QComboBox) as capturing keyboard input. Use it to gate the PTT-hold and CW-momentary keying handlers instead of textInputCaptured(). Arrow-key shortcuts still use textInputCaptured() unchanged, so Up/Down/Left/Right continue to navigate a focused combo's list rather than double-firing as tune/gain shortcuts. Fixes aethersdr#3908
There was a problem hiding this comment.
Reviewed the diff against the codebase — this is a clean, well-scoped fix. Thanks @jensenpat.
The fix is correct. The root cause is accurately diagnosed: textInputCaptured() casts any focused QComboBox as text-input, so the PTT/CW handlers return false, the app-level filter doesn't consume the key, and Qt's default combo behavior opens the dropdown on the first Space. Splitting out textEntryCaptured() and gating only the two keying handlers with it — while leaving textInputCaptured() for the arrow tune/gain shortcuts — is exactly the right surgical boundary. Combo arrow-navigation stays intact.
A few things I verified:
- Editable-combo handling is sound. The PR comment claims an editable
QComboBoxis still caught via its internalQLineEdit. That holds: for an editable comboQApplication::focusWidget()returns the line edit (focus proxy), soqobject_cast<QComboBox*>(w)is false and it falls to theQLineEditbranch → still captures. And even if focus ever landed on the combo itself, thecombo->isEditable()branch covers it. Belt and suspenders, both correct. - Release paths stay fail-safe.
handlePttHoldShortcut's modifier-tolerant release (line 228) still re-checkstextEntryCaptured()before un-keying, and the CW handler's!press && !currentlyActivereturn is updated symmetrically — so press/release gate consistently and TX can't get stuck keyed. - No collateral.
textInputCaptured()is unchanged for every other caller, includes (QComboBox/QLineEdit/QSpinBox) are already present,focusWidget()is null-checked, and the two handlers have no consumers outside the event filter in this file.
No AppSettings/RAII/lifetime concerns — this touches no persistence or ownership. Nothing blocking from me; looks good to merge once hardware/CI verification lands.
🤖 aethersdr-agent · cost: $5.5276 · model: claude-opus-4-8
ten9876
left a comment
There was a problem hiding this comment.
Independently verified against the tree:
- The app-level event filter (MainWindow_Shortcuts.cpp:407-427) sees the key before the focused QComboBox, so consuming it (return true) correctly beats the dropdown — that's the broken link the old textInputCaptured() left open.
- textEntryCaptured() still catches editable combos (isEditable() + their internal QLineEdit), so text entry is unaffected.
- textInputCaptured() is untouched, so arrow-key list navigation / tune-gain suppression are preserved.
- Press/release gates are symmetric across both handlers.
- New Space-with-combo-focused behavior matches the filter's own stated intent ('buttons, combos won't steal Space').
All 6 checks green. LGTM.
#3961) ## Release documentation prep for **v26.7.1** (2026-07-02) 29 commits since v26.6.5. Docs-only + version bump — no code behavior change. ### Version bump 26.6.5 → 26.7.1 The three canonical locations (per `AGENTS.md`): - `CMakeLists.txt:2` — `project(AetherSDR VERSION 26.7.1)` → `AETHERSDR_VERSION` (the app's version string) - `README.md` — Current version line - `AGENTS.md` — Current version line ### `CHANGELOG.md` Promoted `[Unreleased]` → **`## [v26.7.1] — 2026-07-02`** in house style (v-prefix, em-dash, "N commits since…" opener + headline), authored from all merged PRs and categorized: - **Added:** 3D stacked-trace spectrum (#3899), 3D dBm scale (#3937), in-process NVIDIA BNR + AFX download-on-demand (#3902), TX meter mouse-over readouts (#3936), SWR manual sweep range (#3885), CW sidetone capture (#3895), KiwiSDR metadata scaffolding (#3898), bridge verbs (#3920) - **Changed:** BNR container/NIM backend removed, 60 fps + per-pixel GPU FFT trace (#3958), FlexLib-sourced model capabilities (#3954/#2177), RX speaker-latency cut (#3897), Display pane sections (#3935) - **Fixed:** #3922, #3926, #3941, #3940, #3942, #3921, #3903, #3892, #3924, #3891, #3890, #3900, #3947 - Fresh empty `[Unreleased]` restored above the release block. Internal/CI/docs/self-fix PRs (#3931/#3916/#3934/#3929) omitted per house style. ### `ROADMAP.md` - Cycle header → "post-v26.7.1" - NVIDIA BNR removed from **In flight** (shipped this cycle) - Five v26.7.1 highlights prepended to **Recently shipped** ### `README.md` - Highlights: GPU spectrum bullet now notes the **per-pixel FFT trace at up to 60 fps** and the **3D stacked-trace** spectrum mode ### Reviewer notes - Touches protected paths (`*.md` + `AGENTS.md` Tier-1 + `CMakeLists.txt`) — needs `@aethersdr/infrastructure` sign-off. - Tagging `v26.7.1` and cutting the release build are **not** part of this PR (docs prep only). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Summary
After selecting a profile from the Profile Switcher (or any other non-editable
QComboBoxin the app), the first Space press doesn't key PTT — it opens the combo's dropdown instead. A second Space is needed to actually transmit.Root cause
textInputCaptured()(src/gui/MainWindow_Shortcuts.cpp) treats any focusedQComboBoxas a text-input widget:handlePttHoldShortcut()andhandleCwMomentaryShortcut()both bail out (returnfalse, don't consume the key) when this predicate is true. A non-editableQComboBox(like the Profile Switcher's Global/TX/Mic rows) keeps keyboard focus after its popup closes from a mouse selection. So:textInputCaptured()true → handler returnsfalse→ Qt's defaultQComboBoxbehavior opens the dropdown instead of keying TX.The same guard sits in the CW momentary-key handler, so CW keying is swallowed the same way after a profile selection.
Fix
Add
textEntryCaptured(), a narrower predicate that only counts widgets that actually consume typed text:QLineEdit,QTextEdit,QPlainTextEdit,QSpinBox, and an editableQComboBox(its line edit is the real focus target, so it's still caught). A non-editable combo no longer counts.Use
textEntryCaptured()to gate the PTT-hold and CW-momentary keying handlers. LeavetextInputCaptured()(and everywhere else it's used, e.g. arrow-key tune/gain shortcuts) unchanged, so Up/Down/Left/Right still navigate a focused combo's list instead of double-firing as global tune/gain shortcuts.Files changed
src/gui/MainWindowShortcutState.h— declaretextEntryCaptured()src/gui/MainWindow_Shortcuts.cpp— definetextEntryCaptured(); switchhandlePttHoldShortcut()andhandleCwMomentaryShortcut()to use it instead oftextInputCaptured()Test plan
Fixes #3908
💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat