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); }