Skip to content

Add right-click context menu on off-screen slice indicators#1126

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
rfoust:fix/offscreen-slice-click-recenter
Apr 11, 2026
Merged

Add right-click context menu on off-screen slice indicators#1126
ten9876 merged 1 commit into
aethersdr:mainfrom
rfoust:fix/offscreen-slice-click-recenter

Conversation

@rfoust

@rfoust rfoust commented Apr 10, 2026

Copy link
Copy Markdown
Collaborator

Right-clicking an off-screen slice helper icon now shows a context menu with three options:

  • Close Slice — removes the slice receiver
  • Move Slice Here — tunes the slice to the panadapter center frequency
  • Center Slice — recenters the panadapter display around the slice

Left-click behavior (activate slice) is unchanged.

Changes

  • SpectrumWidget.h: Added sliceTuneRequested(int sliceId, double freqMhz) signal
  • SpectrumWidget.cpp: Added right-click context menu for off-screen indicators; gated existing left-click handler to Qt::LeftButton only
  • MainWindow.cpp: Wired sliceTuneRequested to SliceModel::setFrequency()

Right-clicking an off-screen slice helper icon now shows a context menu
with three options:
- Close Slice: removes the slice receiver
- Move Slice Here: tunes the slice to the panadapter center frequency
- Center Slice: recenters the panadapter display around the slice

Left-click behavior (activate slice) is unchanged.
@rfoust rfoust requested a review from ten9876 as a code owner April 10, 2026 14:42
Copilot AI review requested due to automatic review settings April 10, 2026 14:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a right-click context menu to the off-screen slice indicator widgets, enabling quick slice actions (close, move-to-center, center display) while preserving existing left-click activation behavior.

Changes:

  • Added a new SpectrumWidget::sliceTuneRequested(int sliceId, double freqMhz) signal for requesting slice frequency changes.
  • Implemented a right-click context menu on off-screen slice indicators and restricted the existing indicator click handler to Qt::LeftButton.
  • Wired the new signal in MainWindow to tune the slice via SliceModel::setFrequency().

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/gui/SpectrumWidget.h Adds a new signal to request tuning a specific slice to a given MHz frequency.
src/gui/SpectrumWidget.cpp Adds right-click context menu behavior for off-screen slice indicators and gates indicator activation to left-click only.
src/gui/MainWindow.cpp Connects the new tune-request signal to the slice model’s frequency setter.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +916 to +920
for (int oi = 0; oi < m_offScreenRects.size(); ++oi) {
if (!m_offScreenRects[oi].isNull() &&
m_offScreenRects[oi].contains(QPoint(static_cast<int>(ev->position().x()), y))) {
const auto& so = m_sliceOverlays[oi];
if (!so.isActive) emit sliceClicked(so.sliceId);

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

Potential out-of-bounds access: the loop iterates over m_offScreenRects.size(), but then indexes m_sliceOverlays[oi]. If a slice is removed and m_offScreenRects hasn't been resized/rebuilt yet (until next paint), m_offScreenRects can be larger than m_sliceOverlays and a click in a stale rect could crash. Iterate up to min(m_offScreenRects.size(), m_sliceOverlays.size()) or guard oi < m_sliceOverlays.size() before dereferencing.

Copilot uses AI. Check for mistakes.
// Right-click on off-screen slice indicator → slice context menu
for (int oi = 0; oi < m_offScreenRects.size(); ++oi) {
if (!m_offScreenRects[oi].isNull() &&
m_offScreenRects[oi].contains(QPoint(mx, y))) {

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

Same bounds issue here: indexing m_sliceOverlays[oi] while iterating m_offScreenRects can crash if m_offScreenRects is stale after slice removal (until next repaint). Guard the index or keep a rect→sliceId mapping instead of relying on parallel vectors staying in sync across events.

Suggested change
m_offScreenRects[oi].contains(QPoint(mx, y))) {
m_offScreenRects[oi].contains(QPoint(mx, y))) {
if (oi >= m_sliceOverlays.size()) {
continue;
}

Copilot uses AI. Check for mistakes.
Comment thread src/gui/MainWindow.cpp
Comment on lines +5446 to +5450
connect(sw, &SpectrumWidget::sliceTuneRequested,
this, [this](int sliceId, double freqMhz) {
if (auto* s = m_radioModel.slice(sliceId))
s->setFrequency(freqMhz);
});

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

wirePanadapter() is documented as being callable multiple times for the same SpectrumWidget, and other command-sending signals are disconnected first to avoid duplicate sends. Add a matching disconnect(sw, &SpectrumWidget::sliceTuneRequested, this, nullptr) (or use Qt::UniqueConnection) before this connect to prevent multiple setFrequency() calls after re-wiring.

Copilot uses AI. Check for mistakes.

@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 the contribution, @rfoust — this is a clean, well-scoped addition. The left-click guard fix is correct, the lambda captures are all by-value where they need to be, and the null check in MainWindow is handled properly. A couple of things worth discussing:

m_centerMhz mutated directly inside the "Center Slice" action

[this, freq = so.freqMhz]{
    m_centerMhz = freq;          // ← direct widget state mutation
    markOverlayDirty();
    emit centerChangeRequested(m_centerMhz);
}

This is an optimistic update — the widget repaints with the new center before the signal is even dispatched. If the centerChangeRequested handler can reject or adjust the value (e.g. band-edge clamping), the widget momentarily shows a center that the model won't confirm, and the subsequent feedback write will then snap it again. The other two actions go signal-only and let the model drive state. It's worth checking whether center changes elsewhere in SpectrumWidget follow the optimistic-update pattern — if they do, this is fine and consistent; if not, consider removing the direct m_centerMhz = freq and letting the existing centerChangeRequested → model → widget callback path handle the repaint.

sliceId & 3 for the menu label letter

const QChar letter = QChar('A' + (so.sliceId & 3));

Functionally fine for FLEX's 4-slice limit, but worth verifying this matches the letter derivation used in paintOffScreenIndicators() (or wherever the overlay labels are generated) so the menu always shows the same letter the indicator does.

No concerns about scope, resource management, or the signal wiring in MainWindow.cpp. The PR is solid — those two points are the only things I'd want eyes on before merge.

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Hey @rfoust — thanks for the contribution! The CI "failure" here is not a problem with your code. Here's what actually happened:

What failed and why

The only failed step is "Delete artifacts" inside the "Cleanup artifacts" post-build job (run 24248554422). This is a housekeeping step that runs after the real work is done — it tries to clean up temporary build artifacts from the Actions cache. It has no bearing on whether your code builds or is correct.

All the checks that actually matter passed:

Check Result
build ✅ success
check-paths ✅ success
Prepare ✅ success
Agent ✅ success
Upload results ✅ success

The cleanup job likely failed because the artifact had already expired or been deleted by a concurrent run — this is a known flakiness in the artifact-deletion step of the CI infrastructure, not anything you introduced.

What you need to do

Nothing on your end. Your code is fine. A maintainer can re-run just the "Cleanup artifacts" job if they want a clean green checkmark, or simply ignore it — it doesn't block the merge.

Thanks again for taking the time to contribute — the build looks solid!

@aethersdr aethersdr deleted a comment from aethersdr-agent Bot Apr 10, 2026
@aethersdr aethersdr deleted a comment from aethersdr-agent Bot Apr 10, 2026
@ten9876 ten9876 merged commit 6c7c76b into aethersdr:main Apr 11, 2026
8 of 9 checks passed
@rfoust rfoust deleted the fix/offscreen-slice-click-recenter branch April 11, 2026 05:01
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.

3 participants