Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3461,6 +3461,11 @@ MainWindow::MainWindow(QWidget* parent)
// Overlay-menu antenna wiring is now per-pan in wirePanadapter() (#1260).
// Antenna list and S-meter are now wired per-widget in onSliceAdded.

// ── Title bar: Pan Follow ────────────────────────────────────────────────
connect(m_titleBar, &TitleBar::panFollowToggled,
this, &MainWindow::setPanFollow);
if (m_titleBar->isPanFollowChecked()) setPanFollow(true);

// ── Title bar: PC Audio, master volume, headphone volume ────────────────
// The remote_audio_rx stream controls the radio's audio routing:
// stream exists → audio to PC; stream removed → audio to radio speakers.
Expand Down Expand Up @@ -19059,4 +19064,52 @@ void MainWindow::onSpectrumReadyForSHistory(quint32 streamId, const QVector<floa
}
}

// ─── Pan Follow ───────────────────────────────────────────────────────────────

void MainWindow::setPanFollow(bool on)
{
disconnect(m_panFollowConn);
disconnect(m_panFollowSliceConn);

if (!on) return;

// Re-attach helper: wires frequency tracking to whichever slice 0
// is currently live. Called on activation and whenever slice 0 is
// recreated (radio reconnect, slice re-assignment, etc.).
auto attachToSlice0 = [this]() {
disconnect(m_panFollowConn);

auto* s = m_radioModel.slice(0);
if (!s) {
// No slice yet — uncheck the button so UI matches reality.
if (m_titleBar) m_titleBar->setPanFollowChecked(false);
return;
}

auto centerPan = [this, s]() {
const QString panId = s->panId();
if (panId.isEmpty()) return;
const double freq = s->frequency();
auto* pan = m_radioModel.panadapter(panId);
if (pan && qFuzzyCompare(pan->centerMhz(), freq)) return;
const QString freqStr = QString::number(freq, 'f', 6);
if (pan) pan->applyPanStatus({{"center", freqStr}});
m_radioModel.sendCommand(
QString("display pan set %1 center=%2").arg(panId, freqStr));
};

centerPan();
m_panFollowConn = connect(s, &SliceModel::frequencyChanged,
this, [centerPan](double) { centerPan(); });
};

attachToSlice0();

// Re-attach whenever a new slice 0 appears (reconnect / re-assignment).
m_panFollowSliceConn = connect(&m_radioModel, &RadioModel::sliceAdded,
this, [this, attachToSlice0](SliceModel* s) {
if (s && s->sliceId() == 0) attachToSlice0();
});
}

} // namespace AetherSDR
5 changes: 5 additions & 0 deletions src/gui/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,11 @@ private slots:
void onFdvMetersChanged();
#endif

// Pan Follow — keeps the panadapter centered on Slice A frequency
QMetaObject::Connection m_panFollowConn;
QMetaObject::Connection m_panFollowSliceConn;
void setPanFollow(bool on);

#if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE)
DaxBridge* m_daxBridge{nullptr};
QString m_savedMicSelection; // restore on stopDax
Expand Down
45 changes: 45 additions & 0 deletions src/gui/TitleBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <QMouseEvent>
#include <QPointer>
#include <QPushButton>
#include <QSignalBlocker>
#include <QSizePolicy>
#include <QSlider>
#include <QLabel>
Expand Down Expand Up @@ -219,6 +220,38 @@ TitleBar::TitleBar(QWidget* parent)
// ── PC Audio + Master Vol + HP Vol ──────────────────────────────────────
auto& s = AppSettings::instance();

// Pan Follow toggle — keeps the panadapter centered on Slice A frequency
m_panFollowBtn = new QPushButton("Pan Lock");
m_panFollowBtn->setCheckable(true);
bool panLockOn = s.value("PanLockEnabled", "False").toString() == "True";
m_panFollowBtn->setChecked(panLockOn);
m_panFollowBtn->setFixedHeight(22);
m_panFollowBtn->setFixedWidth(70);
m_panFollowBtn->setAccessibleName("Pan follow slice");
m_panFollowBtn->setAccessibleDescription("Keep panadapter centered on Slice A frequency");
m_panFollowBtn->setToolTip("Pan Lock — keeps the panadapter centered on Slice A frequency (e.g. for Doppler tracking)");

auto updatePanFollowStyle = [this]() {
m_panFollowBtn->setStyleSheet(m_panFollowBtn->isChecked()
? "QPushButton { background: #1e4a8a; color: #b0e8ff; border: 1px solid #4090d0; "
"border-radius: 3px; font-size: 10px; font-weight: bold; }"
"QPushButton:hover { background: #2558a0; }"
: "QPushButton { background: #1a2a3a; color: #607080; border: 1px solid #304050; "
"border-radius: 3px; font-size: 10px; font-weight: bold; }"
"QPushButton:hover { background: #243848; }");
};
updatePanFollowStyle();

connect(m_panFollowBtn, &QPushButton::toggled, this, [this, updatePanFollowStyle](bool on) {
updatePanFollowStyle();
auto& ss = AppSettings::instance();
ss.setValue("PanLockEnabled", on ? "True" : "False");
ss.save();
emit panFollowToggled(on);
});
m_hbox->addWidget(m_panFollowBtn);
m_hbox->addSpacing(4);

// PC Audio toggle
m_pcBtn = new QPushButton("PC Audio");
m_pcBtn->setCheckable(true);
Expand Down Expand Up @@ -459,6 +492,18 @@ bool TitleBar::isDragHandle(QObject* obj) const
return obj && obj->property(kTitleDragHandleProperty).toBool();
}

bool TitleBar::isPanFollowChecked() const
{
return m_panFollowBtn && m_panFollowBtn->isChecked();
}

void TitleBar::setPanFollowChecked(bool on)
{
if (!m_panFollowBtn) return;
QSignalBlocker block(m_panFollowBtn);
m_panFollowBtn->setChecked(on);
}

bool TitleBar::isSystemMoveAreaAt(const QPoint& globalPos) const
{
const QPoint localPos = mapFromGlobal(globalPos);
Expand Down
7 changes: 7 additions & 0 deletions src/gui/TitleBar.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ class TitleBar : public QWidget {
// as caption drag zones while keeping controls interactive.
bool isSystemMoveAreaAt(const QPoint& globalPos) const;

// Pan Lock accessors. Out-of-line so the header doesn't need to pull in
// <QPushButton>; defined alongside the button's setup in TitleBar.cpp.
bool isPanFollowChecked() const;
void setPanFollowChecked(bool on);

signals:
void panFollowToggled(bool on);
void pcAudioToggled(bool on);
void masterVolumeChanged(int pct);
void headphoneVolumeChanged(int pct);
Expand Down Expand Up @@ -87,6 +93,7 @@ class TitleBar : public QWidget {
QLabel* m_appNameLabel{nullptr};
QLabel* m_otherTxLabel{nullptr};
QPushButton* m_mfBtn{nullptr};
QPushButton* m_panFollowBtn{nullptr};
QPushButton* m_pcBtn{nullptr};
QPushButton* m_speakerBtn{nullptr};
QPushButton* m_headphoneBtn{nullptr};
Expand Down
Loading