From c5b9504189db06ca5d9a655b482af9d3bc5ffbf5 Mon Sep 17 00:00:00 2001 From: "Jan W. Krieger" Date: Mon, 28 Jan 2019 17:05:03 +0100 Subject: [PATCH] finished rework of user-interaction API for the mouse --- .../test_user_interaction.cpp | 70 ++++- .../test_user_interaction.h | 8 +- lib/jkqtplotter/jkqtplotter.cpp | 249 ++++++++++++------ lib/jkqtplotter/jkqtplotter.h | 178 ++++++++----- 4 files changed, 342 insertions(+), 163 deletions(-) diff --git a/examples/test_user_interaction/test_user_interaction.cpp b/examples/test_user_interaction/test_user_interaction.cpp index 80b99f8e30..362aa1178e 100644 --- a/examples/test_user_interaction/test_user_interaction.cpp +++ b/examples/test_user_interaction/test_user_interaction.cpp @@ -90,12 +90,6 @@ TestUserInteraction::TestUserInteraction(QWidget *parent) : connect(cmbMagnification, SIGNAL(currentIndexChanged(int)), this, SLOT(setPlotMagnification(int))); cmbMagnification->setCurrentIndex(3); - // add a QComboBox that allows to set the left mouse button action for the JKQTPlotter - chkZoomByMouseWheel=new QCheckBox(this); - chkZoomByMouseWheel->setChecked(plot->getZoomByMouseWheel()); - layForm->addRow("zoom by mouse wheel:", chkZoomByMouseWheel); - connect(chkZoomByMouseWheel, SIGNAL(toggled(bool)), plot, SLOT(setZoomByMouseWheel(bool))); - // add a QComboBox that allows to set the left mouse button action for the JKQTPlotter cmbLeftNoModMouseAction=new QComboBox(this); layForm->addRow("mouse action: left-click, no modifiers", cmbLeftNoModMouseAction); @@ -161,6 +155,46 @@ TestUserInteraction::TestUserInteraction(QWidget *parent) : special->addMenu("Special submenu")->addAction("Special subentry 1 (no action!)"); plot->setSpecialContextMenu(special); + // add a QComboBox that allows to set the left mouse button double-click action for the JKQTPlotter + cmbLeftDoubleClickMouseAction=new QComboBox(this); + layForm->addRow("mouse action: left double-click, no modifiers", cmbLeftDoubleClickMouseAction); + cmbLeftDoubleClickMouseAction->addItem("ClickZoomsIn"); + cmbLeftDoubleClickMouseAction->addItem("ClickZoomsOut"); + cmbLeftDoubleClickMouseAction->addItem("ClickOpensContextMenu"); + cmbLeftDoubleClickMouseAction->addItem("ClickOpensSpecialContextMenu"); + cmbLeftDoubleClickMouseAction->addItem("ClickMovesViewport"); + cmbLeftDoubleClickMouseAction->addItem("NoAction"); + cmbLeftDoubleClickMouseAction->setCurrentIndex(4); + connect(cmbLeftDoubleClickMouseAction, SIGNAL(currentIndexChanged(int)), this, SLOT(setLeftDoubleClickMouseAction(int))); + setLeftDoubleClickMouseAction(cmbLeftDoubleClickMouseAction->currentIndex()); + + // add a QComboBox that allows to set the left mouse button double-click action for the JKQTPlotter + cmbRightDoubleClickMouseAction=new QComboBox(this); + layForm->addRow("mouse action: right double-click, no modifiers", cmbRightDoubleClickMouseAction); + cmbRightDoubleClickMouseAction->addItem("ClickZoomsIn"); + cmbRightDoubleClickMouseAction->addItem("ClickZoomsOut"); + cmbRightDoubleClickMouseAction->addItem("ClickOpensContextMenu"); + cmbRightDoubleClickMouseAction->addItem("ClickOpensSpecialContextMenu"); + cmbRightDoubleClickMouseAction->addItem("ClickMovesViewport"); + cmbRightDoubleClickMouseAction->addItem("NoAction"); + cmbRightDoubleClickMouseAction->setCurrentIndex(1); + connect(cmbRightDoubleClickMouseAction, SIGNAL(currentIndexChanged(int)), this, SLOT(setRightDoubleClickMouseAction(int))); + setRightDoubleClickMouseAction(cmbRightDoubleClickMouseAction->currentIndex()); + + + // add a QComboBox that allows to set the mouse wheel action without modifiers + cmbMouseWheelAction=new QComboBox(this); + layForm->addRow("mouse action: mouse wheel, no modifiers", cmbMouseWheelAction); + cmbMouseWheelAction->addItem("ZoomByWheel"); + cmbMouseWheelAction->addItem("PanByWheel"); + cmbMouseWheelAction->addItem("NoAction"); + cmbMouseWheelAction->setCurrentIndex(0); + connect(cmbMouseWheelAction, SIGNAL(currentIndexChanged(int)), this, SLOT(setMouseWheelNoModAction(int))); + setMouseWheelNoModAction(cmbMouseWheelAction->currentIndex()); + + + + // and add a QLabel to show the different events of the JKQTPlotter: labMouseMoved=new QLabel(this); layForm->addRow("last mouse moved:", labMouseMoved); @@ -171,6 +205,7 @@ TestUserInteraction::TestUserInteraction(QWidget *parent) : connect(plot, SIGNAL(plotMouseMove(double, double)), this, SLOT(plotMouseMove(double, double))); connect(plot, SIGNAL(plotMouseClicked(double, double, Qt::KeyboardModifiers , Qt::MouseButton)), this, SLOT(plotMouseClicked(double, double, Qt::KeyboardModifiers, Qt::MouseButton))); connect(plot, SIGNAL(plotMouseDoubleClicked(double, double, Qt::KeyboardModifiers, Qt::MouseButton)), this, SLOT(plotMouseDoubleClicked(double, double, Qt::KeyboardModifiers, Qt::MouseButton))); + connect(plot, SIGNAL(plotMouseWheelOperated(double, double, Qt::KeyboardModifiers, int, int)), this, SLOT(plotMouseWheelOperated(double, double, Qt::KeyboardModifiers, int, int))); connect(plot, SIGNAL(plotNewZoomRectangle(double, double, double, double, Qt::KeyboardModifiers)), this, SLOT(plotNewZoomRectangle(double, double, double, double, Qt::KeyboardModifiers))); connect(plot, SIGNAL(contextMenuOpened(double, double, QMenu*)), this, SLOT(contextMenuOpened(double, double, QMenu*))); connect(plot, SIGNAL(zoomChangedLocally(double, double, double, double, JKQTPlotter*)), this, SLOT(zoomChangedLocally(double, double, double, double, JKQTPlotter*))); @@ -213,6 +248,24 @@ void TestUserInteraction::setRightClickContextMenu(int index) plot->setContextMenuMode(static_cast(index)); } +void TestUserInteraction::setLeftDoubleClickMouseAction(int index) +{ + if (index>=cmbLeftDoubleClickMouseAction->count()-1) plot->deregisterMouseDoubleClickAction(Qt::LeftButton, Qt::NoModifier); + else plot->registerMouseDoubleClickAction(Qt::LeftButton, Qt::NoModifier, static_cast(index)); +} + +void TestUserInteraction::setRightDoubleClickMouseAction(int index) +{ + if (index>=cmbLeftDoubleClickMouseAction->count()-1) plot->deregisterMouseDoubleClickAction(Qt::RightButton, Qt::NoModifier); + else plot->registerMouseDoubleClickAction(Qt::RightButton, Qt::NoModifier, static_cast(index)); +} + +void TestUserInteraction::setMouseWheelNoModAction(int index) +{ + if (index>=cmbMouseWheelAction->count()-1) plot->deregisterMouseWheelAction(Qt::NoModifier); + else plot->registerMouseWheelAction(Qt::NoModifier, static_cast(index)); +} + void TestUserInteraction::plotMouseMove(double x, double y) { labMouseMoved->setText(QString("plotMouseMove(%1, %2)").arg(x).arg(y)); @@ -233,6 +286,11 @@ void TestUserInteraction::plotNewZoomRectangle(double mouseDragRectXStart, doubl labMouseAction->setText(QString("plotNewZoomRectangle(x=%1..%2, y=%3..%4, modifiers=%5)").arg(mouseDragRectXStart).arg(mouseDragRectXEnd).arg(mouseDragRectYStart).arg(mouseDragRectYEnd).arg(KeyboradMod2String(modifiers))); } +void TestUserInteraction::plotMouseWheelOperated(double x, double y, Qt::KeyboardModifiers modifiers, int deltaAngleX, int deltaAngleY) +{ + labMouseAction->setText(QString("plotMouseWheelOperated(x=%1, y%2, modifiers=%3, deltaAngleX=%4, deltaAngleY=%5)").arg(x).arg(y).arg(KeyboradMod2String(modifiers)).arg(deltaAngleX).arg(deltaAngleY)); +} + void TestUserInteraction::contextMenuOpened(double x, double y, QMenu *contextMenu) { contextMenu->addSeparator(); diff --git a/examples/test_user_interaction/test_user_interaction.h b/examples/test_user_interaction/test_user_interaction.h index e0aaabda72..bba230c4ce 100644 --- a/examples/test_user_interaction/test_user_interaction.h +++ b/examples/test_user_interaction/test_user_interaction.h @@ -26,11 +26,15 @@ class TestUserInteraction : public QMainWindow void setRightMouseAction(int index); void setPlotMagnification(int index); void setRightClickContextMenu(int index); + void setLeftDoubleClickMouseAction(int index); + void setRightDoubleClickMouseAction(int index); + void setMouseWheelNoModAction(int index); void plotMouseMove(double x, double y); void plotMouseClicked(double x, double y, Qt::KeyboardModifiers modifiers, Qt::MouseButton button); void plotMouseDoubleClicked(double x, double y, Qt::KeyboardModifiers modifiers, Qt::MouseButton button); void plotNewZoomRectangle(double mouseDragRectXStart, double mouseDragRectXEnd, double mouseDragRectYStart, double mouseDragRectYEnd, Qt::KeyboardModifiers modifiers); + void plotMouseWheelOperated(double x, double y, Qt::KeyboardModifiers modifiers, int deltaAngleX, int deltaAngleY); void contextMenuOpened(double x, double y, QMenu* contextMenu); void zoomChangedLocally(double newxmin, double newxmax, double newymin, double newymax, JKQTPlotter* sender); void userScribbleClick(double x, double y, Qt::KeyboardModifiers modifiers, bool first, bool last); @@ -53,9 +57,11 @@ class TestUserInteraction : public QMainWindow QComboBox* cmbLeftCtrlModMouseAction; QComboBox* cmbRightNoModMouseAction; QComboBox* cmbRightClickContextMenu; + QComboBox* cmbRightDoubleClickMouseAction; + QComboBox* cmbLeftDoubleClickMouseAction; + QComboBox* cmbMouseWheelAction; QCheckBox* chkLogX; QCheckBox* chkLogY; - QCheckBox* chkZoomByMouseWheel; QLabel* labMouseAction; QLabel* labMouseMoved; QLabel* labMouseClicked; diff --git a/lib/jkqtplotter/jkqtplotter.cpp b/lib/jkqtplotter/jkqtplotter.cpp index 02dd30b22d..a9fbf05491 100644 --- a/lib/jkqtplotter/jkqtplotter.cpp +++ b/lib/jkqtplotter/jkqtplotter.cpp @@ -69,7 +69,6 @@ JKQTPlotter::JKQTPlotter(QWidget *parent): void JKQTPlotter::init(bool datastore_internal, QWidget* parent, JKQTPDatastore* datast) { - leftDoubleClickAction=LeftDoubleClickDefault; menuSpecialContextMenu=nullptr; mouseContextX=0; mouseContextY=0; @@ -86,7 +85,6 @@ void JKQTPlotter::init(bool datastore_internal, QWidget* parent, JKQTPDatastore* mousePosX=0; mousePosY=0; - rightMouseButtonAction=JKQTPlotter::RightMouseButtonContextMenu; connect(plotter, SIGNAL(plotUpdated()), this, SLOT(redrawPlot())); connect(plotter, SIGNAL(overlaysUpdated()), this, SLOT(redrawOverlays())); @@ -106,13 +104,17 @@ void JKQTPlotter::init(bool datastore_internal, QWidget* parent, JKQTPDatastore* displayMousePosition=true; displayToolbar=true; toolbarAlwaysOn=false; - contextMenuMode=ContextMenuModes::StandardContextMenu; + + // set default user-interactions: + contextMenuMode=ContextMenuModes::StandardContextMenu; registerMouseDragAction(Qt::LeftButton, Qt::NoModifier, MouseDragActions::ZoomRectangle); registerMouseDragAction(Qt::LeftButton, Qt::ControlModifier, MouseDragActions::PanPlotOnMove); + registerMouseDoubleClickAction(Qt::LeftButton, Qt::NoModifier, MouseDoubleClickActions::ClickMovesViewport); + registerMouseWheelAction(Qt::NoModifier, MouseWheelActions::ZoomByWheel); - zoomByMouseWheel=true; - + // enable mouse-tracking, so mouseMoved-Events can be caught setMouseTracking(true); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); toolbar=new JKVanishQToolBar(this); @@ -228,6 +230,24 @@ void JKQTPlotter::setUserActionCompositionMode(const QPainter::CompositionMode & } } + + +void JKQTPlotter::registerMouseWheelAction(Qt::KeyboardModifier modifier, JKQTPlotter::MouseWheelActions action) +{ + registeredMouseWheelActions[modifier]=action; +} + +void JKQTPlotter::deregisterMouseWheelAction(Qt::KeyboardModifier modifier) +{ + registeredMouseWheelActions.remove(modifier); +} + +void JKQTPlotter::clearAllMouseWheelActions() +{ + registeredMouseWheelActions.clear(); +} + + QPainter::CompositionMode JKQTPlotter::getUserActionCompositionMode() const { return this->userActionCompositionMode; @@ -382,7 +402,7 @@ void JKQTPlotter::mousePressEvent ( QMouseEvent * event ){ currentMouseDragAction.clear(); auto actionIT=findMatchingMouseDragAction(event->button(), event->modifiers()); - if (actionIT!=registeredMouseActionModes.end()) { + if (actionIT!=registeredMouseDragActionModes.end()) { // we found a matching action currentMouseDragAction=MouseDragAction(actionIT.key().first, actionIT.key().second, actionIT.value()); mouseLastClickX=event->x(); @@ -470,41 +490,55 @@ void JKQTPlotter::mouseReleaseEvent ( QMouseEvent * event ){ } void JKQTPlotter::mouseDoubleClickEvent ( QMouseEvent * event ){ + + auto itAction=findMatchingMouseDoubleClickAction(event->button(), event->modifiers()); + if (itAction!=registeredMouseDoubleClickActions.end()) { + // we found an action to perform on this double-click + if (itAction.value()==MouseDoubleClickActions::ClickOpensContextMenu) { + openStandardContextMenu(event->x(), event->y()); + } else if (itAction.value()==MouseDoubleClickActions::ClickOpensSpecialContextMenu) { + openSpecialContextMenu(event->x(), event->y()); + } else if (itAction.value()==MouseDoubleClickActions::ClickZoomsIn || itAction.value()==MouseDoubleClickActions::ClickZoomsOut) { + double factor=4.0; + if (itAction.value()==MouseDoubleClickActions::ClickZoomsOut) factor=1; + + double xmin=plotter->p2x(static_cast(event->x())/magnification-static_cast(plotter->getPlotWidth())/factor); + double xmax=plotter->p2x(static_cast(event->x())/magnification+static_cast(plotter->getPlotWidth())/factor); + double ymin=plotter->p2y(static_cast(event->y())/magnification-static_cast(getPlotYOffset())+static_cast(plotter->getPlotHeight())/factor); + double ymax=plotter->p2y(static_cast(event->y())/magnification-static_cast(getPlotYOffset())-static_cast(plotter->getPlotHeight())/factor); + if ( (event->x()/magnificationgetInternalPlotBorderLeft()) || (event->x()/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + xmin=getXMin(); + xmax=getXMax(); + } else if (((event->y()-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((event->y()-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + ymin=getYMin(); + ymax=getYMax(); + } + plotter->setXY(xmin, xmax, ymin, ymax); + update(); + } else if (itAction.value()==MouseDoubleClickActions::ClickMovesViewport) { + QRectF zoomRect= QRectF(QPointF(plotter->x2p(getXAxis()->getMin()),plotter->y2p(getYAxis()->getMax())), QPointF(plotter->x2p(getXAxis()->getMax()),plotter->y2p(getYAxis()->getMin()))); + if ( (event->x()/magnificationgetInternalPlotBorderLeft()) || (event->x()/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + zoomRect.moveCenter(QPointF(zoomRect.center().x(), event->y())); + } else if (((event->y()-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((event->y()-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + zoomRect.moveCenter(QPointF(event->x(), zoomRect.center().y())); + } else { + zoomRect.moveCenter(QPointF(event->x(), event->y())); + } + setXY(plotter->p2x(zoomRect.left()), plotter->p2x(zoomRect.right()), plotter->p2y(zoomRect.bottom()), plotter->p2y(zoomRect.top())); + } + } + // only react on double clicks inside the widget if ( (event->x()/magnification>=plotter->getInternalPlotBorderLeft()) && (event->x()/magnification<=plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) && ((event->y()-getPlotYOffset())/magnification>=plotter->getInternalPlotBorderTop()) && ((event->y()-getPlotYOffset())/magnification<=plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { mouseLastClickX=event->x(); mouseLastClickY=event->y(); - if (event->button()==Qt::LeftButton) { - if (leftDoubleClickAction==LeftDoubleClickContextMenu) { - openStandardContextMenu(event->x(), event->y()); - event->accept(); - } else if (leftDoubleClickAction==LeftDoubleClickSpecialContextMenu) { - openSpecialContextMenu(event->x(), event->y()); - event->accept(); - } - } - if (rightMouseButtonAction==JKQTPlotter::RightMouseButtonZoom && event->button()==Qt::RightButton) { - double factor=4.0; - if (event->button()==Qt::RightButton) factor=1; - double xmin=plotter->p2x((long)round(static_cast(event->x())/magnification-static_cast(plotter->getPlotWidth())/factor)); - double xmax=plotter->p2x((long)round(static_cast(event->x())/magnification+static_cast(plotter->getPlotWidth())/factor)); - double ymin=plotter->p2y((long)round(static_cast(event->y())/magnification-static_cast(getPlotYOffset())+static_cast(plotter->getPlotHeight())/factor)); - double ymax=plotter->p2y((long)round(static_cast(event->y())/magnification-static_cast(getPlotYOffset())-static_cast(plotter->getPlotHeight())/factor)); - - event->accept(); - //xAxis->setRange(xmin, xmax); - //yAxis->setRange(ymin, ymax); - //redrawPlot(); - /*if (plotter->isEmittingSignalsEnabled())*/ emit zoomChangedLocally(xmin, xmax, ymin, ymax, this); - plotter->setXY(xmin, xmax, ymin, ymax); - update(); - } emit plotMouseDoubleClicked(plotter->p2x(event->x()/magnification), plotter->p2y((event->y()-getPlotYOffset())/magnification), event->modifiers(), event->button()); - } else { event->ignore(); } + } + event->accept(); updateCursor(); currentMouseDragAction.clear(); } @@ -524,20 +558,42 @@ void JKQTPlotter::keyReleaseEvent(QKeyEvent *event) { } void JKQTPlotter::wheelEvent ( QWheelEvent * event ) { - // only react on wheel turns inside the widget, turning forward will zoom out and turning backwards will zoom in by a factor of 2 - if ( zoomByMouseWheel && ((event->x()/magnification>=plotter->getInternalPlotBorderLeft()) && (event->x()/magnification<=plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) && - ((event->y()-getPlotYOffset())/magnification>=plotter->getInternalPlotBorderTop()) && ((event->y()-getPlotYOffset())/magnification<=plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) ) { - double factor=pow(2.0, 1.0*static_cast(event->delta())/120.0)*2.0; - double xmin=plotter->p2x((long)round(static_cast(event->x())/magnification-static_cast(plotter->getPlotWidth())/factor)); - double xmax=plotter->p2x((long)round(static_cast(event->x())/magnification+static_cast(plotter->getPlotWidth())/factor)); - double ymin=plotter->p2y((long)round(static_cast(event->y())/magnification-static_cast(getPlotYOffset())+static_cast(plotter->getPlotHeight())/factor)); - double ymax=plotter->p2y((long)round(static_cast(event->y())/magnification-static_cast(getPlotYOffset())-static_cast(plotter->getPlotHeight())/factor)); - event->accept(); - plotter->setXY(xmin, xmax, ymin, ymax); - } else { - event->ignore(); + auto itAction=findMatchingMouseWheelAction(event->modifiers()); + if (itAction!=registeredMouseWheelActions.end()) { + if (itAction.value()==MouseWheelActions::ZoomByWheel) { + double factor=pow(2.0, 1.0*static_cast(event->delta())/120.0)*2.0; + double xmin=plotter->p2x(static_cast(event->x())/magnification-static_cast(plotter->getPlotWidth())/factor); + double xmax=plotter->p2x(static_cast(event->x())/magnification+static_cast(plotter->getPlotWidth())/factor); + double ymin=plotter->p2y(static_cast(event->y())/magnification-static_cast(getPlotYOffset())+static_cast(plotter->getPlotHeight())/factor); + double ymax=plotter->p2y(static_cast(event->y())/magnification-static_cast(getPlotYOffset())-static_cast(plotter->getPlotHeight())/factor); + if ( (event->x()/magnificationgetInternalPlotBorderLeft()) || (event->x()/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + xmin=getXMin(); + xmax=getXMax(); + } else if (((event->y()-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((event->y()-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + ymin=getYMin(); + ymax=getYMax(); + } + plotter->setXY(xmin, xmax, ymin, ymax); + } else if (itAction.value()==MouseWheelActions::PanByWheel) { + QPoint d=event->pixelDelta(); + QRectF zoomRect= QRectF(QPointF(plotter->x2p(getXAxis()->getMin()),plotter->y2p(getYAxis()->getMax())), QPointF(plotter->x2p(getXAxis()->getMax()),plotter->y2p(getYAxis()->getMin()))); + if ( (event->x()/magnificationgetInternalPlotBorderLeft()) || (event->x()/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + zoomRect.translate(0, d.y()); + } else if (((event->y()-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((event->y()-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + zoomRect.translate(d.x(), 0); + } else { + zoomRect.translate(d.x(), d.y()); + } + setXY(plotter->p2x(zoomRect.left()), plotter->p2x(zoomRect.right()), plotter->p2y(zoomRect.bottom()), plotter->p2y(zoomRect.top())); + } } + + + event->accept(); + + emit plotMouseWheelOperated(plotter->p2x(event->x()), plotter->p2x(event->y()), event->modifiers(), event->angleDelta().x(), event->angleDelta().y()); + updateCursor(); currentMouseDragAction.clear(); } @@ -873,31 +929,10 @@ void JKQTPlotter::reactGraphVisible(bool visible) } } -void JKQTPlotter::setRightMouseButtonAction(const JKQTPlotter::RightMouseButtonAction &__value) -{ - this->rightMouseButtonAction = __value; -} - void JKQTPlotter::setContextMenuMode(JKQTPlotter::ContextMenuModes mode) { contextMenuMode=mode; } - -JKQTPlotter::RightMouseButtonAction JKQTPlotter::getActionRightMouseButton() const -{ - return this->rightMouseButtonAction; -} - -void JKQTPlotter::setLeftDoubleClickAction(const JKQTPlotter::LeftDoubleClickAction &__value) -{ - this->leftDoubleClickAction = __value; -} - -JKQTPlotter::LeftDoubleClickAction JKQTPlotter::getActionLeftDoubleClick() const -{ - return this->leftDoubleClickAction; -} - QMenu *JKQTPlotter::getSpecialContextMenu() const { return this->menuSpecialContextMenu; } @@ -911,16 +946,6 @@ void JKQTPlotter::setSpecialContextMenu(QMenu *menu) } } -void JKQTPlotter::setZoomByMouseWheel(bool __value) -{ - this->zoomByMouseWheel = __value; -} - -bool JKQTPlotter::getZoomByMouseWheel() const -{ - return this->zoomByMouseWheel; -} - double JKQTPlotter::getMouseContextX() const { return this->mouseContextX; } @@ -1063,24 +1088,59 @@ void JKQTPlotter::openStandardAndSpecialContextMenu(int x, int y) QHash, JKQTPlotter::MouseDragActions>::const_iterator JKQTPlotter::findMatchingMouseDragAction(Qt::MouseButton button, Qt::KeyboardModifiers modifiers) const { if (modifiers.testFlag(Qt::ShiftModifier)) { - return registeredMouseActionModes.find(qMakePair(button, Qt::ShiftModifier)); + return registeredMouseDragActionModes.find(qMakePair(button, Qt::ShiftModifier)); } else if (modifiers.testFlag(Qt::ControlModifier)) { - return registeredMouseActionModes.find(qMakePair(button, Qt::ControlModifier)); + return registeredMouseDragActionModes.find(qMakePair(button, Qt::ControlModifier)); } else if (modifiers.testFlag(Qt::AltModifier)) { - return registeredMouseActionModes.find(qMakePair(button, Qt::AltModifier)); + return registeredMouseDragActionModes.find(qMakePair(button, Qt::AltModifier)); } else if (modifiers.testFlag(Qt::MetaModifier)) { - return registeredMouseActionModes.find(qMakePair(button, Qt::MetaModifier)); + return registeredMouseDragActionModes.find(qMakePair(button, Qt::MetaModifier)); } else if (modifiers.testFlag(Qt::KeypadModifier)) { - return registeredMouseActionModes.find(qMakePair(button, Qt::KeypadModifier)); + return registeredMouseDragActionModes.find(qMakePair(button, Qt::KeypadModifier)); } else if (modifiers.testFlag(Qt::GroupSwitchModifier)) { - return registeredMouseActionModes.find(qMakePair(button, Qt::GroupSwitchModifier)); + return registeredMouseDragActionModes.find(qMakePair(button, Qt::GroupSwitchModifier)); } else { - return registeredMouseActionModes.find(qMakePair(button, Qt::NoModifier)); + return registeredMouseDragActionModes.find(qMakePair(button, Qt::NoModifier)); } - - return registeredMouseActionModes.end(); } +QHash, JKQTPlotter::MouseDoubleClickActions>::const_iterator JKQTPlotter::findMatchingMouseDoubleClickAction(Qt::MouseButton button, Qt::KeyboardModifiers modifiers) const +{ + if (modifiers.testFlag(Qt::ShiftModifier)) { + return registeredMouseDoubleClickActions.find(qMakePair(button, Qt::ShiftModifier)); + } else if (modifiers.testFlag(Qt::ControlModifier)) { + return registeredMouseDoubleClickActions.find(qMakePair(button, Qt::ControlModifier)); + } else if (modifiers.testFlag(Qt::AltModifier)) { + return registeredMouseDoubleClickActions.find(qMakePair(button, Qt::AltModifier)); + } else if (modifiers.testFlag(Qt::MetaModifier)) { + return registeredMouseDoubleClickActions.find(qMakePair(button, Qt::MetaModifier)); + } else if (modifiers.testFlag(Qt::KeypadModifier)) { + return registeredMouseDoubleClickActions.find(qMakePair(button, Qt::KeypadModifier)); + } else if (modifiers.testFlag(Qt::GroupSwitchModifier)) { + return registeredMouseDoubleClickActions.find(qMakePair(button, Qt::GroupSwitchModifier)); + } else { + return registeredMouseDoubleClickActions.find(qMakePair(button, Qt::NoModifier)); + } +} + +QHash::const_iterator JKQTPlotter::findMatchingMouseWheelAction(Qt::KeyboardModifiers modifiers) const +{ + if (modifiers.testFlag(Qt::ShiftModifier)) { + return registeredMouseWheelActions.find(Qt::ShiftModifier); + } else if (modifiers.testFlag(Qt::ControlModifier)) { + return registeredMouseWheelActions.find(Qt::ControlModifier); + } else if (modifiers.testFlag(Qt::AltModifier)) { + return registeredMouseWheelActions.find(Qt::AltModifier); + } else if (modifiers.testFlag(Qt::MetaModifier)) { + return registeredMouseWheelActions.find(Qt::MetaModifier); + } else if (modifiers.testFlag(Qt::KeypadModifier)) { + return registeredMouseWheelActions.find(Qt::KeypadModifier); + } else if (modifiers.testFlag(Qt::GroupSwitchModifier)) { + return registeredMouseWheelActions.find(Qt::GroupSwitchModifier); + } else { + return registeredMouseWheelActions.find(Qt::NoModifier); + } +} void JKQTPlotter::setPlotUpdateEnabled(bool enable) { @@ -1091,17 +1151,32 @@ void JKQTPlotter::setPlotUpdateEnabled(bool enable) void JKQTPlotter::registerMouseDragAction(Qt::MouseButton button, Qt::KeyboardModifier modifier, JKQTPlotter::MouseDragActions action) { - registeredMouseActionModes[qMakePair(button, modifier)]=action; + registeredMouseDragActionModes[qMakePair(button, modifier)]=action; } void JKQTPlotter::deregisterMouseDragAction(Qt::MouseButton button, Qt::KeyboardModifier modifier) { - registeredMouseActionModes.remove(qMakePair(button, modifier)); + registeredMouseDragActionModes.remove(qMakePair(button, modifier)); } void JKQTPlotter::clearAllRegisteredMouseDragActions() { - registeredMouseActionModes.clear(); + registeredMouseDragActionModes.clear(); +} + +void JKQTPlotter::registerMouseDoubleClickAction(Qt::MouseButton button, Qt::KeyboardModifier modifier, JKQTPlotter::MouseDoubleClickActions action) +{ + registeredMouseDoubleClickActions[qMakePair(button, modifier)]=action; +} + +void JKQTPlotter::deregisterMouseDoubleClickAction(Qt::MouseButton button, Qt::KeyboardModifier modifier) +{ + registeredMouseDoubleClickActions.remove(qMakePair(button, modifier)); +} + +void JKQTPlotter::clearAllRegisteredMouseDoubleClickActions() +{ + registeredMouseDoubleClickActions.clear(); } diff --git a/lib/jkqtplotter/jkqtplotter.h b/lib/jkqtplotter/jkqtplotter.h index 2d0c5ed909..e6a7ad145f 100644 --- a/lib/jkqtplotter/jkqtplotter.h +++ b/lib/jkqtplotter/jkqtplotter.h @@ -156,6 +156,9 @@ LIB_EXPORT void initJKQTPlotterResources(); * * \subsection JKQTPLOTTER_USERMOUSEINTERACTION Mouse-Interaction in JKQTPlotter * + * \see \ref JKQTPlotterUserInteraction + * + * \subsubsection JKQTPLOTTER_USERMOUSEINTERACTION_MOUSEDRAG Actions When Dragging the Mouse * JKQTPlotter offers several methods that allow to customize, how it reacts to mouse actions: * - registerMouseDragAction() tells JKQTPlotter to perform a certain action (selected from JKQTPlotter::MouseActionMode) * when a specified mouse button is pushed while a specified (or no) keyborad modifiers (e.g. CTRL,ALT,SHIFT...) is pressed. @@ -166,10 +169,13 @@ LIB_EXPORT void initJKQTPlotterResources(); * \endcode * Therefore by default you can draw a zoom rectangle with the left mouse button without modifiers * and you can pan/drag the plot with the left mouse-button while keeping CTRL pressed. - * - deregisterMouseDragAction() deletes a previously defined unser-interaction + * - deregisterMouseDragAction() deletes a previously defined user-interaction * - clearAllRegisteredMouseDragActions() deletes all previously specified user-actions * . * + * Pressing the \c ESC key will stop the current JKQTPlotter::MouseActionMode. + * + * \subsubsection JKQTPLOTTER_USERMOUSEINTERACTION_MOUSECLICK Actions After Clicks on the Mouse Buttons * The right mouse button has a special role: If it is single-clicked and no JKQTPlotter::MouseActionMode is specified * for the vent, it opens the context menu, unless you call \c setContextMenuMoode(JKQTPlotter::NoContextMenu) . * You can also use setContextMenuMoode() to specify which type of context menu is shown. See JKQTPlotter::ContextMenuModes @@ -179,13 +185,43 @@ LIB_EXPORT void initJKQTPlotterResources(); * - plotMouseClicked() for any single-click (during the pressDown-Event!) * - plotMouseDoubleClicked() for any double-click * . + * + * The reaction to double-clicks of the mouse buttons can be specified separately. The possible + * actions are listed in JKQTPlotter::MouseDoubleClickActions. You can bind one of these actions + * to a specific click with these functions: + * - registerMouseDoubleClickAction() tells JKQTPlotter to perform a certain action (selected from JKQTPlotter::MouseDoubleClickActions) + * when a specified mouse button is pushed while a specified (or no) keyborad modifiers (e.g. CTRL,ALT,SHIFT...) is pressed. + * By default JKQTPlotter calls this in its constructors: + * \code + * registerMouseDoubleClickAction(Qt::LeftButton, Qt::NoModifier, JKQTPlotter::MouseDoubleClickActions::ClickMovesViewport); + * \endcode + * Therefore by default you can pan the plot/move the viewport by double clicking a location + * - deregisterMouseDoubleClickAction() deletes a previously defined user-interaction + * - clearAllRegisteredMouseDoubleClickAction() deletes all previously specified user-actions + * . + * The button to react to is specified as a parameter. + * + * \subsubsection JKQTPLOTTER_USERMOUSEINTERACTION_MOUSEWHEEL Actions When a Mouse Wheel Event Occurs + * The actions to be performed when the mouse hweel is operated are specified in JKQTPlotter::MouseWheelActions. + * You can bind one of these actions to the mouse-wheel (under the condition that a specified Qt::KeyboardModifier + * is pressed) by using these functions: + * - registerMouseWheelAction() tells JKQTPlotter to perform a certain action (selected from JKQTPlotter::MouseWheelActions) + * when a specified mouse button is pushed while a specified (or no) keyborad modifiers (e.g. CTRL,ALT,SHIFT...) is pressed. + * By default JKQTPlotter calls this in its constructors: + * \code + * registerMouseWheelAction(Qt::NoModifier, JKQTPlotter::MouseWheelActions::ZoomByWheel); + * \endcode + * Therefore by default you can zoom into and out of the plot, using the mouse wheel. + * - deregisterMouseWheelAction() deletes a previously defined user-interaction + * - clearAllMouseWheelActions() deletes all previously specified user-actions + * . + * In addition the signal void plotMouseWheelOperated() is emitted whenever a mouse-wheel event occurs. + * + * + * \subsubsection JKQTPLOTTER_USERMOUSEINTERACTION_MOUSEMOVE Signaling When Mouse Moves * In addition the signal plotMouseMove() is called whenever the mouse moves over the plot. * Additional signals may be emitted, depending on the currently active JKQTPlotter::MouseActionMode. * - * Pressing the ESC key will stop the current JKQTPlotter::MouseActionMode. - * - * \see \ref JKQTPlotterUserInteraction - * * * * \section JKQTPLOTTER_USEQTCREATOR How to use JKQTPlotter in the Qt Form Designer @@ -228,18 +264,17 @@ class LIB_EXPORT JKQTPlotter: public QWidget { /** \brief actions that can be bound to a double-click of the mouse */ enum MouseDoubleClickActions { - ClickZoomsIn=0, /*!< \brief a (double-)click zooms into the plot at the current mouse location */ - ClickZoomsOut, /*!< \brief a (double-)click zooms out of the plot at the current mouse location */ - ClickOpensContextMenu, /*!< \brief a (double-)click opens the context menu */ - ClickOpensSpecialContextMenu, /*!< \brief a (double-)click opens the special context menu \see setSpecialContextMenu() */ - NoClickAction, /*!< \brief no action is performed */ + ClickZoomsIn=0, /*!< \brief a double-click zooms into the plot at the current mouse location */ + ClickZoomsOut, /*!< \brief a double-click zooms out of the plot at the current mouse location */ + ClickOpensContextMenu, /*!< \brief a double-click opens the context menu */ + ClickOpensSpecialContextMenu, /*!< \brief a double-click opens the special context menu \see setSpecialContextMenu() */ + ClickMovesViewport, /*!< \brief a double-click centers the x/y-range around the clicked position */ }; /** \brief actions that can be bound to a mouse wheel event */ enum MouseWheelActions { ZoomByWheel=0, /*!< \brief use the mouse-wheel for zooming */ PanByWheel, /*!< \brief use the mouse-wheel for panning the plot */ - NoWheelAction, /*!< \brief no mouse-wheel action */ }; /** \brief modes for the context menu */ @@ -254,22 +289,6 @@ class LIB_EXPORT JKQTPlotter: public QWidget { - /** \brief options of how to react to a right mouse button click */ - enum RightMouseButtonAction { - RightMouseButtonNone=0, /*!< \brief do not perform any action on a right mouse button click */ - RightMouseButtonZoom=1, /*!< \brief use right mouse button for zoomin out */ - RightMouseButtonContextMenu=2 /*!< \brief show the context menu when clicking the right mouse button */ - }; - - /** \brief options of how to react to a left mouse button double-click (single-click events are described by MouseActionMode) */ - enum LeftDoubleClickAction { - LeftDoubleClickDefault, /*!< \brief */ - LeftDoubleClickContextMenu, /*!< \brief open the context menu when the left mouse button is double-clicked */ - LeftDoubleClickSpecialContextMenu, /*!< \brief open the special context menu when the left mouse button is double-clicked */ - }; - - - /** \brief class constructor * @@ -397,31 +416,37 @@ class LIB_EXPORT JKQTPlotter: public QWidget { */ void setPlotUpdateEnabled(bool enable); - /** \brief registeres a certain mouse action \a action to be executed when a mouse drag operation is + /** \brief registeres a certain mouse drag action \a action to be executed when a mouse drag operation is * initialized with the given \a button and \a modifier */ void registerMouseDragAction(Qt::MouseButton button, Qt::KeyboardModifier modifier, MouseDragActions action); - /** \brief deregisteres the mouse action to be executed when a mouse drag operation is + /** \brief deregisteres the mouse drag action to be executed when a mouse drag operation is * initialized with the given \a button and \a modifier */ void deregisterMouseDragAction(Qt::MouseButton button, Qt::KeyboardModifier modifier); - /** \brief clear all registeres mouse actions */ + /** \brief clear all registeres mouse drag actions */ void clearAllRegisteredMouseDragActions(); - /*! \brief returns the property rightMouseButtonAction ( \copybrief rightMouseButtonAction ). - \details Description of the parameter rightMouseButtonAction is:
\copydoc rightMouseButtonAction
- \see rightMouseButtonAction for more information */ - RightMouseButtonAction getActionRightMouseButton() const; - /*! \brief returns the property leftDoubleClickAction ( \copybrief leftDoubleClickAction ). - \details Description of the parameter leftDoubleClickAction is:
\copydoc leftDoubleClickAction
- \see leftDoubleClickAction for more information */ - LeftDoubleClickAction getActionLeftDoubleClick() const; + + /** \brief registeres a certain mouse action \a action to be executed when a mouse double-click occurs + * with the given \a button and \a modifier */ + void registerMouseDoubleClickAction(Qt::MouseButton button, Qt::KeyboardModifier modifier, MouseDoubleClickActions action); + /** \brief deregisteres the mouse action \a action to be executed when a mouse double-click occurs + * with the given \a button and \a modifier */ + void deregisterMouseDoubleClickAction(Qt::MouseButton button, Qt::KeyboardModifier modifier); + /** \brief clear all registered mouse double-click actions */ + void clearAllRegisteredMouseDoubleClickActions(); + + /** \brief specifies the action to perform on a mouse wheel event when a given modifier is pressed \see deregisterMouseWheelAction(), clearAllMouseWheelActions(), JKQTPLOTTER_USERMOUSEINTERACTION */ + void registerMouseWheelAction(Qt::KeyboardModifier modifier, MouseWheelActions action); + /** \brief deletes all mouse-wheel actions registered for a given \a modifier \see registerMouseWheelAction(), clearAllMouseWheelActions(), JKQTPLOTTER_USERMOUSEINTERACTION */ + void deregisterMouseWheelAction(Qt::KeyboardModifier modifier); + /** \brief deletes all mouse-wheel actions \see registerMouseWheelAction(), deregisterMouseWheelAction(), JKQTPLOTTER_USERMOUSEINTERACTION */ + void clearAllMouseWheelActions(); + /*! \brief returns the currently set special context menu object */ QMenu *getSpecialContextMenu() const; /*! \brief sets a QMenu object to be used as special context menu */ void setSpecialContextMenu(QMenu* menu); - /*! \brief returns the property zoomByMouseWheel ( \copybrief zoomByMouseWheel ). - \details Description of the parameter zoomByMouseWheel is:
\copydoc zoomByMouseWheel
- \see zoomByMouseWheel for more information */ - bool getZoomByMouseWheel() const; + /** \brief returns the property mouseContextX ( \copybrief mouseContextX ). \details Description of the parameter mouseContextX is:
\copydoc mouseContextX
. @@ -638,23 +663,6 @@ class LIB_EXPORT JKQTPlotter: public QWidget { \see userActionCompositionMode for more information */ void setUserActionCompositionMode(const QPainter::CompositionMode & __value); - - /*! \brief sets the property zoomByMouseWheel ( \copybrief zoomByMouseWheel ) to the specified \a __value. - \details Description of the parameter zoomByMouseWheel is:
\copydoc zoomByMouseWheel
- \see zoomByMouseWheel for more information */ - void setZoomByMouseWheel(bool __value); - - /*! \brief sets the property leftDoubleClickAction ( \copybrief leftDoubleClickAction ) to the specified \a __value. - \details Description of the parameter leftDoubleClickAction is:
\copydoc leftDoubleClickAction
- \see leftDoubleClickAction for more information */ - void setLeftDoubleClickAction(const LeftDoubleClickAction & __value); - - /*! \brief sets the property rightMouseButtonAction ( \copybrief rightMouseButtonAction ) to the specified \a __value. - \details Description of the parameter rightMouseButtonAction is:
\copydoc rightMouseButtonAction
- \see rightMouseButtonAction for more information */ - void setRightMouseButtonAction(const RightMouseButtonAction & __value); - - /** \brief sets the mode if the standard context menu \see ContextMenuModes, JKQTPLOTTER_USERMOUSEINTERACTION */ void setContextMenuMode(ContextMenuModes mode); @@ -729,6 +737,15 @@ class LIB_EXPORT JKQTPlotter: public QWidget { * \param button mouse-button that was used for the click */ void plotMouseDoubleClicked(double x, double y, Qt::KeyboardModifiers modifiers, Qt::MouseButton button); + /** \brief emitted when a single-click event from the mouse occurs inside the plot + * + * \param x x-position of the mouse (in plot coordinates) + * \param y y-position of the mouse (in plot coordinates) + * \param modifiers key-modifiers when the click occured + * \param deltaAngleX amount of rotation (in eighths of a degree) of the wheel in x-direction + * \param deltaAngleY amount of rotation (in eighths of a degree) of the wheel in y-direction + */ + void plotMouseWheelOperated(double x, double y, Qt::KeyboardModifiers modifiers, int deltaAngleX, int deltaAngleY); /** \brief emitted when the mouse action JKQTPlotter::ZoomRectangle and the drawing of the new zoom rectangle is finished (=mouse key released) * * \param mouseDragRectXStart start of the selected x-range (in plot coordinates) @@ -839,12 +856,25 @@ class LIB_EXPORT JKQTPlotter: public QWidget { /** \brief the currently executed MouseDragAction */ MouseDragAction currentMouseDragAction; - /** \brief lists all availble mouse action modes */ - QHash, MouseDragActions> registeredMouseActionModes; + /** \brief lists all availble mouse drag action modes */ + QHash, MouseDragActions> registeredMouseDragActionModes; /** \brief searches registeredMouseActionModes for a matching action */ QHash, MouseDragActions>::const_iterator findMatchingMouseDragAction(Qt::MouseButton button, Qt::KeyboardModifiers modifiers) const; + /** \brief lists all availble mouse wheel action modes */ + QHash registeredMouseWheelActions; + + /** \brief searches registeredMouseWheelActions for a matching action */ + QHash::const_iterator findMatchingMouseWheelAction(Qt::KeyboardModifiers modifiers) const; + + + /** \brief action to perform on a double-click of the mouse buttons (depending on the button and the modifiers) */ + QHash, MouseDoubleClickActions> registeredMouseDoubleClickActions; + + /** \brief searches registeredMouseDoubleClickActions for a matching action */ + QHash, MouseDoubleClickActions>::const_iterator findMatchingMouseDoubleClickAction(Qt::MouseButton button, Qt::KeyboardModifiers modifiers) const; + /** \brief you may overwrite this method to modify the given context emnu before it is displayed. * * The plotter will fill the menu with the default items and then call this method. The default implementation does NOTHING. @@ -918,14 +948,6 @@ class LIB_EXPORT JKQTPlotter: public QWidget { */ QImage oldImage; - /** \brief indicates whether zooming in by double clicking and zooming out by right-clicking is activated */ - RightMouseButtonAction rightMouseButtonAction; - - /** \brief indicates whether zooming using the mouse-wheel is activated */ - bool zoomByMouseWheel; - - /** \brief indicates the action to perform on a left mouse-button double-click */ - LeftDoubleClickAction leftDoubleClickAction; /** \brief use this QMenu instance instead of the standard context emnu of this widget */ QMenu* menuSpecialContextMenu; @@ -1093,4 +1115,22 @@ inline uint qHash(const QPair &key, uint s return static_cast(key.first)+static_cast(key.second); } +/** \brief qHash-variant used by JKQTPlotter + * \internal + * \ingroup jkqtpplotterclasses +*/ +template<> +inline uint qHash(const Qt::MouseButton &key, uint /*seed*/ ) noexcept(noexcept(qHash(key))) { + return static_cast(key); +} + +/** \brief qHash-variant used by JKQTPlotter + * \internal + * \ingroup jkqtpplotterclasses +*/ +template<> +inline uint qHash(const Qt::KeyboardModifier &key, uint /*seed*/ ) noexcept(noexcept(qHash(key))) { + return static_cast(key); +} + #endif // JKQTPLOTTER_H