Skip to content

fix(spectrum): Pan Lock stands down during slice drag, recenters on release#3786

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
svabi79:fix-panlock-slice-drag
Jun 27, 2026
Merged

fix(spectrum): Pan Lock stands down during slice drag, recenters on release#3786
ten9876 merged 1 commit into
aethersdr:mainfrom
svabi79:fix-panlock-slice-drag

Conversation

@svabi79

@svabi79 svabi79 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #3785.

With Pan Lock on, dragging a slice made the panadapter jump in large lurching steps instead of scrolling smoothly, and afterwards Pan Lock stopped keeping the slice centered (button lit) until toggled off/on. Two independently-developed features both drive the pan center and fought each other:

The edge-pan path tried to "avoid pan-follow" but the bypass was incomplete — it still calls setFrequency(), which emits frequencyChanged, and Pan Lock is wired straight to that signal, so both fired each tick with opposite intents. The in-window drag had the matching symptom (slice snapping back to center instead of following the cursor).

Fix: Pan Lock stands down for the whole duration of a slice drag (in-window tune and edge auto-pan), then recenters once on release so Pan Lock re-asserts. SpectrumWidget emits sliceDragActiveChanged(bool) on slice-drag start/end; MainWindow suppresses the recenter while the flag is set and recenters on release. The recenter logic is extracted into MainWindow::recenterPanFollowOnSlice0() (called on enable, on slice-0 frequencyChanged, and once on drag-end), self-guarded on the Pan Lock toggle state. This mirrors the existing WFM precedence (WFM already makes Pan Lock stand down).

This is a UX precedence choice (during a drag the slice follows the cursor / the band scrolls; on release the slice snaps back to center). Flagged for maintainer confirmation, consistent with the WFM-precedence note already in the code.

Measurements

i9-13900K / RTX 4090, GPU path, dummy load, Pan Lock on; gated aether.perf drag=1 windows + the SliceDrag center trace.

before after
in-window drag — panCenterCmdRate ~34 / s (Pan Lock fights every move) 0–2 / s (stands down)
in-window drag — pan center snaps to slice each move stable (slice follows cursor)
edge drag — pan center steps ~0.33 MHz lurches smooth (single controller)
on release panCenterCmds=0 → slice left off-centre exactly 1 recenter → slice re-centered

Verified on real hardware: no jumping during in-window or edge drag, and Pan Lock stays active after release (no toggle needed).

Constitution principle honored

Principle XI — Fixes Are Demonstrated (before/after measured via gated aether.perf, table above). Principle VIII — Evidence Over Assertion (root cause and fix confirmed by instrumented measurement on real hardware).

Test plan

  • Local build passes (Windows, Qt 6.8.3, GPU path on)
  • Behavior verified on a real radio / dummy load — in-window + edge drag smooth; Pan Lock re-asserts on release; normal tuning still recenters; WFM precedence intact
  • Existing tests pass (CI)
  • Reproduction steps documented in the linked issue

Checklist

  • Commits are signed (SSH)
  • No new flat-key AppSettings calls (N/A)
  • Code is clean-room (Principle IV)
  • All meter UI uses MeterSmoother (N/A — no meter changes)
  • Documentation updated if user-visible behavior changed (N/A — restores intended Pan Lock behavior; no new user-facing controls)
  • Security-sensitive changes reference a GHSA if applicable (N/A)

…elease (aethersdr#3785)

With Pan Lock on, dragging a slice made the pan lurch in ~0.33 MHz steps / jump,
and afterwards Pan Lock stopped centering the slice until toggled. Two features
both drove the pan center: Pan Lock (aethersdr#3408) recenters on every slice-0
frequencyChanged; edge auto-pan (aethersdr#3581) advances the center and parks the slice
at the edge. The edge-pan "avoid pan-follow" bypass was incomplete -- it still
calls setFrequency(), which emits frequencyChanged, which Pan Lock is wired to,
so both fired each tick with opposite intents (slice-at-center vs slice-at-edge).

SpectrumWidget emits sliceDragActiveChanged(bool) on slice-drag start/end.
MainWindow suppresses the Pan Lock recenter for the drag's whole duration and
recenters once on release (recenterPanFollowOnSlice0(), extracted + self-guarded
on the toggle state). Mirrors the existing WFM precedence.

Measured (aether.perf, Pan Lock on): in-window panCenterCmdRate 34 -> 0-2/s,
center stable instead of snapping to slice; edge drag smooth instead of
~0.33 MHz lurches; exactly one recenter on release so Pan Lock re-asserts.

Principle XI -- Fixes Are Demonstrated (before/after measured on real hardware).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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.

Thanks for this @svabi79 — clear root-cause writeup, and the fix is well-architected. I traced the diff against the surrounding code on main; it holds up.

What works well

  • Single source of truth for the recenter. Extracting recenterPanFollowOnSlice0() and self-guarding it on m_panFollowActive (and m_sliceDragInProgress, and the existing WFM precedence) means all three call sites — enable, slice-0 frequencyChanged, and drag-end — go through one guarded path. That's cleaner than the captured centerPan lambda it replaces, and refetching slice(0) fresh each call rather than capturing s by value is the right move.
  • Drag-end signalling is sound. m_draggingVfo is set true at both drag-start sites (in-window passband + slice-flag hit) and is only ever cleared in mouseReleaseEvent (line 6361). The earlier early-returns in that handler are all mutually-exclusive drag states (TNF / divider / dBm / time-scale / bandwidth), so the VFO release always reaches the new emit sliceDragActiveChanged(false). No double-emit, no missed end.
  • Mirrors the documented WFM precedence, and the UX call (slice follows cursor during drag, snaps to center on release) is reasonable and flagged for maintainer sign-off — consistent with the existing convention.
  • CI is green across build / check-windows / check-macos / analyze(cpp) / CodeQL.

One edge case worth a note (non-blocking)

The defensive m_sliceDragInProgress = false in setPanFollow() covers drag-abandoned-then-Pan-Lock-toggled. But if a VFO drag is ever torn down without a mouseReleaseEvent and without a subsequent Pan Lock toggle — e.g. the pane is removed mid-drag, or the mouse grab is lost — m_sliceDragInProgress sticks true and Pan Lock stays silently suppressed until the next toggle. In practice Qt delivers the release to the grabbing widget so this is narrow, and there's no leaveEvent/hideEvent path that resets m_draggingVfo today, so it's unlikely to trigger. Just flagging it; if you wanted belt-and-suspenders, clearing the flag wherever a drag could be cancelled would close it, but I don't think it blocks merge.

Minor observation (not a bug): sliceDragActiveChanged fires from every panadapter's SpectrumWidget into the single shared m_sliceDragInProgress, so dragging a slice on a non-Slice-A pane also stands Pan Lock down for that drag. It's a harmless no-op — recenter only triggers on slice-0 frequencyChanged, which a different slice's drag won't emit, and the release-time recenter is qFuzzyCompare-guarded.

Nicely measured and scoped. 👍


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

@rfoust rfoust assigned rfoust and unassigned rfoust Jun 27, 2026

@rfoust rfoust left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I reviewed this against the current merge ref and I do not see a merge blocker.

The change is nicely scoped: SpectrumWidget now reports the full VFO drag lifetime, and MainWindow centralizes Pan Lock recentering through recenterPanFollowOnSlice0(), guarded by Pan Lock state, drag state, and the existing WFM precedence. That fixes the root conflict cleanly: during drag, the drag controller owns pan movement; on release, Pan Lock gets one chance to reassert.

Validation:

  • Built the PR merge ref successfully with cmake --build build --target AetherSDR --parallel 8
  • Ran the automation bridge smoke checks: ping, dumpTree, Pan Lock invoke, and tools/automation_validate.py
  • Confirmed automation stayed non-transmitting
  • Ran git diff --check
  • Ran python3 tools/check_a11y.py; only existing repo-wide warnings, none in the touched files
  • Manually tested the slice-drag/Pan Lock behavior and it worked correctly

The remaining abandoned-drag edge case already noted looks narrow and non-blocking. From my review and manual test, this looks ready to merge once maintainer approval is satisfied.

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Reviewed the diff and tested live on a FLEX-8600: with Pan Lock on, dragging Slice A now stands down the per-tick recenter for the whole drag (no more ~0.33 MHz pan lurching/jumping) and recenters once on release so Pan Lock re-asserts.

  • Clean refactor: the inline centerPan lambda becomes recenterPanFollowOnSlice0(), called on enable, on slice-0 frequency change, and once on drag-end.
  • Drag state is driven by a new SpectrumWidget::sliceDragActiveChanged(bool) emitted on VFO-drag start (marker + filter-passband paths) and release; m_sliceDragInProgress suppresses recentering during the drag.
  • setPanFollow() resets m_sliceDragInProgress defensively (covers a drag abandoned without a mouse-release).
  • WFM precedence preserved (WFM-held pan still wins; Pan Follow stands down).

Not a keying path (Principle VI N/A). CI green on all 6. Thanks @svabi79 — nice fix for a real annoyance.

@ten9876 ten9876 merged commit 135574b into aethersdr:main Jun 27, 2026
6 checks passed
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.

Pan Lock: dragging a slice to the panadapter edge jumps in large steps, then Pan Lock stops working

3 participants