diff --git a/plotjuggler_app/mainwindow.cpp b/plotjuggler_app/mainwindow.cpp
index 446724dba..975f9093a 100644
--- a/plotjuggler_app/mainwindow.cpp
+++ b/plotjuggler_app/mainwindow.cpp
@@ -822,6 +822,10 @@ void MainWindow::onPlotAdded(PlotWidget* plot)
plot->setDefaultStyle(ui->buttonDots->isChecked() ? PlotWidgetBase::LINES_AND_DOTS :
PlotWidgetBase::LINES);
+ QSettings settings;
+ bool swap_pan_zoom = settings.value("Preferences::swap_pan_zoom", false).toBool();
+ plot->setSwapZoomPan(swap_pan_zoom);
+
if (ui->buttonReferencePoint->isChecked() && _reference_tracker_time.has_value())
{
plot->onReferenceLineChecked(ui->buttonReferencePoint->isChecked(),
@@ -3141,6 +3145,7 @@ void MainWindow::on_actionPreferences_triggered()
{
QSettings settings;
QString prev_style = settings.value("Preferences::theme", "light").toString();
+ bool prev_swap_pan_zoom = settings.value("Preferences::swap_pan_zoom", false).toBool();
PreferencesDialog dialog;
dialog.exec();
@@ -3151,6 +3156,16 @@ void MainWindow::on_actionPreferences_triggered()
{
loadStyleSheet(tr(":/resources/stylesheet_%1.qss").arg(theme));
}
+
+ // Apply swap pan/zoom preference to all existing plots
+ bool swap_pan_zoom = settings.value("Preferences::swap_pan_zoom", false).toBool();
+ if (swap_pan_zoom != prev_swap_pan_zoom)
+ {
+ auto visitor = [swap_pan_zoom](PlotWidget* plot) {
+ plot->setSwapZoomPan(swap_pan_zoom);
+ };
+ forEachWidget(visitor);
+ }
}
void MainWindow::on_playbackStep_valueChanged(double step)
diff --git a/plotjuggler_app/plotwidget.cpp b/plotjuggler_app/plotwidget.cpp
index 38fe18727..ba41d3e77 100644
--- a/plotjuggler_app/plotwidget.cpp
+++ b/plotjuggler_app/plotwidget.cpp
@@ -1683,14 +1683,6 @@ bool PlotWidget::eventFilter(QObject* obj, QEvent* event)
return false;
}
-void PlotWidget::overrideCursonMove()
-{
- QSettings settings;
- QString theme = settings.value("Preferences::theme", "light").toString();
- auto pixmap = LoadSvg(":/resources/svg/move_view.svg", theme);
- QApplication::setOverrideCursor(QCursor(pixmap.scaled(24, 24)));
-}
-
void PlotWidget::setAxisScale(QwtAxisId axisId, double min, double max)
{
if (min > max)
@@ -1746,17 +1738,12 @@ bool PlotWidget::canvasEventFilter(QEvent* event)
emit trackerMoved(pointF);
return true; // don't pass to canvas().
}
- else if (mouse_event->modifiers() == Qt::ControlModifier) // panner
- {
- overrideCursonMove();
- }
return false; // send to canvas()
}
else if (mouse_event->buttons() == Qt::MiddleButton &&
mouse_event->modifiers() == Qt::NoModifier)
{
- overrideCursonMove();
- return false;
+ return false; // send to canvas()
}
else if (mouse_event->button() == Qt::RightButton)
{
@@ -1797,7 +1784,6 @@ bool PlotWidget::canvasEventFilter(QEvent* event)
case QEvent::MouseButtonRelease: {
if (_dragging.mode == DragInfo::NONE)
{
- QApplication::restoreOverrideCursor();
return false;
}
}
diff --git a/plotjuggler_app/plotwidget.h b/plotjuggler_app/plotwidget.h
index fdeed4c80..543ab2003 100644
--- a/plotjuggler_app/plotwidget.h
+++ b/plotjuggler_app/plotwidget.h
@@ -247,7 +247,6 @@ private slots:
// void updateMaximumZoomArea();
void rescaleEqualAxisScaling();
- void overrideCursonMove();
void setAxisScale(QwtAxisId axisId, double min, double max);
};
diff --git a/plotjuggler_app/preferences_dialog.cpp b/plotjuggler_app/preferences_dialog.cpp
index bb2b0f726..d9f478001 100644
--- a/plotjuggler_app/preferences_dialog.cpp
+++ b/plotjuggler_app/preferences_dialog.cpp
@@ -17,6 +17,8 @@ PreferencesDialog::PreferencesDialog(QWidget* parent)
{
ui->setupUi(this);
QSettings settings;
+
+ // Apperance
QString theme = settings.value("Preferences::theme", "light").toString();
if (theme == "dark")
{
@@ -27,9 +29,19 @@ PreferencesDialog::PreferencesDialog(QWidget* parent)
ui->comboBoxTheme->setCurrentIndex(0);
}
+ bool use_separator = settings.value("Preferences::use_separator", true).toBool();
+ ui->checkBoxSeparator->setChecked(use_separator);
+
+ bool use_opengl = settings.value("Preferences::use_opengl", true).toBool();
+ ui->checkBoxOpenGL->setChecked(use_opengl);
+
int precision = settings.value("Preferences::precision", 3).toInt();
ui->comboBoxPrecision->setCurrentIndex(precision);
+ bool no_splash = settings.value("Preferences::no_splash", false).toBool();
+ ui->checkBoxSkipSplash->setChecked(no_splash);
+
+ // Behavior
bool use_plot_color_index = settings.value("Preferences::use_plot_color_index", false).toBool();
bool remember_color = settings.value("Preferences::remember_color", true).toBool();
@@ -37,18 +49,6 @@ PreferencesDialog::PreferencesDialog(QWidget* parent)
ui->radioLocalColorIndex->setChecked(use_plot_color_index);
ui->radioGlobalColorIndex->setChecked(!use_plot_color_index);
- ui->pushButtonAdd->setIcon(LoadSvg(":/resources/svg/add_tab.svg", theme));
- ui->pushButtonRemove->setIcon(LoadSvg(":/resources/svg/trash.svg", theme));
-
- bool use_separator = settings.value("Preferences::use_separator", true).toBool();
- ui->checkBoxSeparator->setChecked(use_separator);
-
- bool use_opengl = settings.value("Preferences::use_opengl", true).toBool();
- ui->checkBoxOpenGL->setChecked(use_opengl);
-
- bool no_splash = settings.value("Preferences::no_splash", false).toBool();
- ui->checkBoxSkipSplash->setChecked(no_splash);
-
bool autozoom_visibility = settings.value("Preferences::autozoom_visibility", true).toBool();
ui->checkBoxAutoZoomVisibility->setChecked(autozoom_visibility);
@@ -59,14 +59,21 @@ PreferencesDialog::PreferencesDialog(QWidget* parent)
settings.value("Preferences::autozoom_filter_applied", true).toBool();
ui->checkBoxAutoZoomFilter->setChecked(autozoom_filter_applied);
- bool truncation_check = settings.value("Preferences::truncation_check", true).toBool();
- ui->checkBoxTruncation->setChecked(truncation_check);
-
QSize export_plot =
settings.value("Preferences::export_plot_size", default_document_dimentions).toSize();
ui->spinBoxExportX->setValue(export_plot.width());
ui->spinBoxExportY->setValue(export_plot.height());
+ bool swap_pan_zoom = settings.value("Preferences::swap_pan_zoom", false).toBool();
+ ui->checkBoxSwapPanZoom->setChecked(swap_pan_zoom);
+
+ bool truncation_check = settings.value("Preferences::truncation_check", true).toBool();
+ ui->checkBoxTruncation->setChecked(truncation_check);
+
+ // Plugins
+ ui->pushButtonAdd->setIcon(LoadSvg(":/resources/svg/add_tab.svg", theme));
+ ui->pushButtonRemove->setIcon(LoadSvg(":/resources/svg/trash.svg", theme));
+
//---------------
auto custom_plugin_folders = settings.value("Preferences::plugin_folders", true).toStringList();
for (const auto& folder : custom_plugin_folders)
@@ -113,6 +120,7 @@ void PreferencesDialog::on_buttonBox_accepted()
settings.setValue("Preferences::truncation_check", ui->checkBoxTruncation->isChecked());
settings.setValue("Preferences::export_plot_size",
QSize{ ui->spinBoxExportX->value(), ui->spinBoxExportY->value() });
+ settings.setValue("Preferences::swap_pan_zoom", ui->checkBoxSwapPanZoom->isChecked());
QStringList plugin_folders;
for (int row = 0; row < ui->listWidgetCustom->count(); row++)
diff --git a/plotjuggler_app/preferences_dialog.ui b/plotjuggler_app/preferences_dialog.ui
index b8b1f5853..3f0ddf3cd 100644
--- a/plotjuggler_app/preferences_dialog.ui
+++ b/plotjuggler_app/preferences_dialog.ui
@@ -7,7 +7,7 @@
0
0
545
- 545
+ 600
@@ -452,6 +452,28 @@
+ -
+
+
+ Mouse Interaction:
+
+
+
-
+
+
+ <html><head/><body><p>Default: Left-click to zoom, Ctrl+Left-click to pan</p><p>Swapped: Left-click to pan, Ctrl+Left-click to zoom</p></body></html>
+
+
+ Swap pan/zoom mouse action modifiers
+
+
+ false
+
+
+
+
+
+
-
diff --git a/plotjuggler_base/include/PlotJuggler/plotwidget_base.h b/plotjuggler_base/include/PlotJuggler/plotwidget_base.h
index a433db80e..5591fa19b 100644
--- a/plotjuggler_base/include/PlotJuggler/plotwidget_base.h
+++ b/plotjuggler_base/include/PlotJuggler/plotwidget_base.h
@@ -104,6 +104,8 @@ class PlotWidgetBase : public QWidget
bool isZoomEnabled() const;
+ void setSwapZoomPan(bool swapped);
+
bool isXYPlot() const;
QRectF currentBoundingRect() const;
@@ -166,6 +168,8 @@ public slots:
PlotLegend* legend();
PlotZoomer* zoomer();
PlotMagnifier* magnifier();
+ PlotPanner* panner1();
+ PlotPanner* panner2();
void updateMaximumZoomArea();
diff --git a/plotjuggler_base/src/plotpanner.cpp b/plotjuggler_base/src/plotpanner.cpp
index 4651f830f..8c19090f5 100644
--- a/plotjuggler_base/src/plotpanner.cpp
+++ b/plotjuggler_base/src/plotpanner.cpp
@@ -3,6 +3,10 @@
#include "qwt_scale_div.h"
#include "plotpanner.h"
+#include
+#include
+#include
+#include "PlotJuggler/svg_util.h"
void PlotPanner::moveCanvas(int dx, int dy)
{
@@ -61,3 +65,35 @@ void PlotPanner::moveCanvas(int dx, int dy)
plot->setAutoReplot(doAutoReplot);
plot->replot();
}
+
+void PlotPanner::widgetMousePressEvent(QMouseEvent* event)
+{
+ // Check if this event matches our panning button/modifiers
+ Qt::MouseButton button;
+ Qt::KeyboardModifiers modifiers;
+ getMouseButton(button, modifiers);
+
+ if (event->button() == button && event->modifiers() == modifiers)
+ {
+ // Set the move cursor when panning starts
+ QSettings settings;
+ QString theme = settings.value("Preferences::theme", "light").toString();
+ auto pixmap = LoadSvg(":/resources/svg/move_view.svg", theme);
+ QApplication::setOverrideCursor(QCursor(pixmap.scaled(24, 24)));
+ _cursor_overridden = true;
+ }
+
+ QwtPlotPanner::widgetMousePressEvent(event);
+}
+
+void PlotPanner::widgetMouseReleaseEvent(QMouseEvent* event)
+{
+ // Restore cursor before calling base class
+ if (_cursor_overridden)
+ {
+ QApplication::restoreOverrideCursor();
+ _cursor_overridden = false;
+ }
+
+ QwtPlotPanner::widgetMouseReleaseEvent(event);
+}
diff --git a/plotjuggler_base/src/plotpanner.h b/plotjuggler_base/src/plotpanner.h
index 5b97e31e3..30cfacdb1 100644
--- a/plotjuggler_base/src/plotpanner.h
+++ b/plotjuggler_base/src/plotpanner.h
@@ -18,7 +18,12 @@ public Q_SLOTS:
signals:
void rescaled(QRectF new_size);
+protected:
+ void widgetMousePressEvent(QMouseEvent* event) override;
+ void widgetMouseReleaseEvent(QMouseEvent* event) override;
+
private:
+ bool _cursor_overridden = false;
};
#endif // PLOTPANNER_H
diff --git a/plotjuggler_base/src/plotwidget_base.cpp b/plotjuggler_base/src/plotwidget_base.cpp
index ceb4f1050..cbbfe8086 100644
--- a/plotjuggler_base/src/plotwidget_base.cpp
+++ b/plotjuggler_base/src/plotwidget_base.cpp
@@ -780,6 +780,22 @@ bool PlotWidgetBase::isZoomEnabled() const
return p->zoom_enabled;
}
+void PlotWidgetBase::setSwapZoomPan(bool swapped)
+{
+ if (swapped)
+ {
+ // Swap: Left button for pan, Ctrl+Left for zoom
+ p->zoomer->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton, Qt::ControlModifier);
+ p->panner1->setMouseButton(Qt::LeftButton, Qt::NoModifier);
+ }
+ else
+ {
+ // Default: Left button for zoom, Ctrl+Left for pan
+ p->zoomer->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton, Qt::NoModifier);
+ p->panner1->setMouseButton(Qt::LeftButton, Qt::ControlModifier);
+ }
+}
+
void PlotWidgetBase::overrideCurvesStyle(std::optional style)
{
p->overridden_curve_style = style;
@@ -818,6 +834,7 @@ PlotLegend* PlotWidgetBase::legend()
{
return p->legend;
}
+
PlotZoomer* PlotWidgetBase::zoomer()
{
return p->zoomer;
@@ -828,6 +845,16 @@ PlotMagnifier* PlotWidgetBase::magnifier()
return p->magnifier;
}
+PlotPanner* PlotWidgetBase::panner1()
+{
+ return p->panner1;
+}
+
+PlotPanner* PlotWidgetBase::panner2()
+{
+ return p->panner2;
+}
+
void PlotWidgetBase::updateMaximumZoomArea()
{
QRectF max_rect;
diff --git a/plotjuggler_base/src/plotzoomer.cpp b/plotjuggler_base/src/plotzoomer.cpp
index add5dba46..752dfcaef 100644
--- a/plotjuggler_base/src/plotzoomer.cpp
+++ b/plotjuggler_base/src/plotzoomer.cpp
@@ -83,7 +83,11 @@ void PlotZoomer::widgetMouseMoveEvent(QMouseEvent* me)
void PlotZoomer::widgetMouseReleaseEvent(QMouseEvent* me)
{
_mouse_pressed = false;
- _zoom_enabled = false;
+ if (_zoom_enabled)
+ {
+ QApplication::restoreOverrideCursor();
+ _zoom_enabled = false;
+ }
QwtPlotPicker::widgetMouseReleaseEvent(me);
this->setTrackerMode(AlwaysOff);
}