fix(slice): spawn slice on empty-pan click instead of hijacking active slice (#3086). Principle I.#3123
Conversation
…e slice (#3086). Principle I. When a user closes the last slice on a panadapter with the VFO ✕ button, the pan stays open by design (matches SmartSDR). The click-to-tune resolver in wirePanadapter previously fell back to activeSlice() when no slice existed on the clicked pan, which dragged a slice from a different pan onto the empty pan's frequency and silently switched bands. Make resolveSpectrumTuneTarget return nullptr for the empty-pan case and let each gesture handler decide: - frequencyClicked → addSliceOnPan(panId, mhz) creates a new slice on the empty pan via slice create pan=<id> freq=<f>, matching FlexLib's Radio.RequestSlice(pan, freq, …) shape (Principle I: FlexLib is the protocol authority). Max-slices enforcement comes from the existing sliceCreateFailed signal path. - incrementalTuneRequested → no-op. A stray wheel scroll shouldn't conjure a slice; click is the explicit "create here" affordance. The "pan not in stack" path keeps the legacy activeSlice() fallback. Blast radius: addSliceOnPan impact risk_score=0.21, 4 high-risk affected (top: MainWindow ctor, wirePanadapter, RadioModel::sendCmd). All three already call addSliceOnPan today — this change only adds a new call site in a local lambda. No new external symbol references, no test-target link surface change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Looks good — the fix is well-targeted and the rationale lines up with the documented recenter policy.
What I verified:
RadioModel::addSliceOnPan(panId, freqMhz)exists atsrc/models/RadioModel.cpp:1441and is the canonical "create slice at a specific frequency on a specific pan" entry point — matches the call pattern already used elsewhere inMainWindow.cpp:12257.- It emits
sliceCreateFailedon radio rejection, so the comment about honoring the max-slices limit is accurate. - The
nullptrsentinel for "empty pan, caller decides" is preferable to returning the wrong-pan active slice, which was the root cause of #3086. - The click-spawn / wheel-no-op asymmetry matches SmartSDR behavior as called out in the doc update — a stray scroll over an empty pan shouldn't conjure a slice.
Minor note (non-blocking): in the click handler's empty-pan branch, resolveClickedPanId() gets called twice (once inside resolveSpectrumTuneTarget, once after it returns null). Iterating m_panStack->allApplets() twice is harmless given the small number of pans, but you could thread the resolved panId through if you wanted to avoid it. Not worth a respin.
Thanks for keeping the legacy "pan not in stack" fallback to activeSlice() intact — that path is the truly-unknown-pan case and is distinct from the empty-pan case the fix targets.
🤖 aethersdr-agent · cost: $3.4830 · model: claude-opus-4-7
…e slice (aethersdr#3086). Principle I. (aethersdr#3123) ## Summary Fixes aethersdr#3086 ### What was changed fix(slice): spawn slice on empty-pan click instead of hijacking active slice (aethersdr#3086). Principle I. ### Files modified - `docs/architecture/recenter-policy.md` - `src/gui/MainWindow.cpp` ``` docs/architecture/recenter-policy.md | 6 ++++++ src/gui/MainWindow.cpp | 32 ++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 8 deletions(-) ``` --- Generated by AetherClaude (automated agent for AetherSDR) --- <sub>🤖 aethersdr-agent · cost: $8.4429 · model: claude-opus-4-7</sub> Co-authored-by: aethersdr-agent[bot] <273844287+aethersdr-agent[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Fixes #3086
What was changed
fix(slice): spawn slice on empty-pan click instead of hijacking active slice (#3086). Principle I.
Files modified
docs/architecture/recenter-policy.mdsrc/gui/MainWindow.cppGenerated by AetherClaude (automated agent for AetherSDR)
🤖 aethersdr-agent · cost: $8.4429 · model: claude-opus-4-7