File indexing completed on 2024-05-12 04:19:52
0001 /* 0002 Gwenview: an image viewer 0003 Copyright 2019 Steffen Hartleib <steffenhartleib@t-online.de> 0004 0005 This program is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU General Public License 0007 as published by the Free Software Foundation; either version 2 0008 of the License, or (at your option) any later version. 0009 0010 This program is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 GNU General Public License for more details. 0014 0015 You should have received a copy of the GNU General Public License 0016 along with this program; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0018 0019 */ 0020 // Self 0021 #include "twofingerpan.h" 0022 0023 // Qt 0024 #include <QGraphicsWidget> 0025 #include <QTouchEvent> 0026 #include <QVector2D> 0027 0028 // KF 0029 0030 // Local 0031 #include "gwenview_lib_debug.h" 0032 #include "lib/touch/touch_helper.h" 0033 0034 namespace Gwenview 0035 { 0036 struct TwoFingerPanRecognizerPrivate { 0037 TwoFingerPanRecognizer *q = nullptr; 0038 bool mTargetIsGrapicsWidget = false; 0039 qint64 mTouchBeginnTimestamp; 0040 bool mGestureTriggered; 0041 qint64 mLastTouchTimestamp; 0042 }; 0043 0044 TwoFingerPanRecognizer::TwoFingerPanRecognizer() 0045 : QGestureRecognizer() 0046 , d(new TwoFingerPanRecognizerPrivate) 0047 { 0048 d->q = this; 0049 } 0050 0051 TwoFingerPanRecognizer::~TwoFingerPanRecognizer() 0052 { 0053 delete d; 0054 } 0055 0056 QGesture *TwoFingerPanRecognizer::create(QObject *) 0057 { 0058 return static_cast<QGesture *>(new TwoFingerPan()); 0059 } 0060 0061 QGestureRecognizer::Result TwoFingerPanRecognizer::recognize(QGesture *state, QObject *watched, QEvent *event) 0062 { 0063 // Because of a bug in Qt in a gesture event in a graphicsview, all gestures are trigger twice 0064 // https://bugreports.qt.io/browse/QTBUG-13103 0065 if (qobject_cast<QGraphicsWidget *>(watched)) 0066 d->mTargetIsGrapicsWidget = true; 0067 if (d->mTargetIsGrapicsWidget && watched->isWidgetType()) 0068 return Ignore; 0069 0070 switch (event->type()) { 0071 case QEvent::TouchBegin: { 0072 auto touchEvent = static_cast<QTouchEvent *>(event); 0073 d->mTouchBeginnTimestamp = touchEvent->timestamp(); 0074 d->mLastTouchTimestamp = touchEvent->timestamp(); 0075 d->mGestureTriggered = false; 0076 state->setProperty("delayActive", true); 0077 state->setHotSpot(touchEvent->touchPoints().first().screenPos()); 0078 return TriggerGesture; 0079 } 0080 0081 case QEvent::TouchUpdate: { 0082 auto touchEvent = static_cast<QTouchEvent *>(event); 0083 const qint64 now = touchEvent->timestamp(); 0084 const QPointF pos = touchEvent->touchPoints().first().pos(); 0085 state->setHotSpot(touchEvent->touchPoints().first().screenPos()); 0086 0087 if (touchEvent->touchPoints().size() >> 2) { 0088 if (d->mGestureTriggered) { 0089 d->mGestureTriggered = false; 0090 } 0091 return CancelGesture; 0092 } 0093 0094 if (touchEvent->touchPoints().size() == 2) { 0095 if (touchEvent->touchPointStates() & Qt::TouchPointReleased) { 0096 if (d->mGestureTriggered) { 0097 d->mGestureTriggered = false; 0098 return FinishGesture; 0099 } 0100 } 0101 0102 if (now - d->mTouchBeginnTimestamp >= Touch_Helper::Touch::gestureDelay) { 0103 state->setProperty("delayActive", false); 0104 0105 // Check if both touch points moving in the same direction 0106 const QVector2D vectorTouchPoint1 = QVector2D(touchEvent->touchPoints().at(0).startPos() - touchEvent->touchPoints().at(0).pos()); 0107 const QVector2D vectorTouchPoint2 = QVector2D(touchEvent->touchPoints().at(1).startPos() - touchEvent->touchPoints().at(1).pos()); 0108 QVector2D dotProduct = vectorTouchPoint1 * vectorTouchPoint2; 0109 0110 // The dot product is greater than zero if both touch points moving in the same direction 0111 // special case if the touch point moving exact or almost exact in x or y axis 0112 // one value of the dot product is zero or a little bit less than zero and 0113 // the other value is bigger than zero 0114 if (dotProduct.x() >= -500 && dotProduct.x() <= 0 && dotProduct.y() > 0) 0115 dotProduct.setX(1); 0116 if (dotProduct.y() >= -500 && dotProduct.y() <= 0 && dotProduct.x() > 0) 0117 dotProduct.setY(1); 0118 0119 if (dotProduct.x() > 0 && dotProduct.y() > 0) { 0120 const QPointF diff = (touchEvent->touchPoints().first().lastPos() - pos); 0121 state->setProperty("delta", diff); 0122 d->mGestureTriggered = true; 0123 return TriggerGesture; 0124 } else { 0125 // special case if the user makes a very slow pan gesture, the vectors a very short. Sometimes the 0126 // dot product is then zero, so we only want to cancel the gesture if the user makes a bigger wrong gesture 0127 if (vectorTouchPoint1.toPoint().manhattanLength() > 50 || vectorTouchPoint2.toPoint().manhattanLength() > 50) { 0128 d->mGestureTriggered = false; 0129 return CancelGesture; 0130 } else { 0131 const QPointF diff = (touchEvent->touchPoints().first().lastPos() - pos); 0132 state->setProperty("delta", diff); 0133 d->mGestureTriggered = true; 0134 return TriggerGesture; 0135 } 0136 } 0137 } else { 0138 state->setProperty("delta", QPointF(0, 0)); 0139 state->setProperty("delayActive", true); 0140 d->mGestureTriggered = false; 0141 return TriggerGesture; 0142 } 0143 } 0144 break; 0145 } 0146 0147 case QEvent::TouchEnd: { 0148 auto touchEvent = static_cast<QTouchEvent *>(event); 0149 if (d->mGestureTriggered) { 0150 d->mGestureTriggered = false; 0151 state->setHotSpot(touchEvent->touchPoints().first().screenPos()); 0152 return FinishGesture; 0153 } 0154 break; 0155 } 0156 0157 default: 0158 return Ignore; 0159 } 0160 return Ignore; 0161 } 0162 0163 TwoFingerPan::TwoFingerPan(QObject *parent) 0164 : QGesture(parent) 0165 { 0166 } 0167 0168 } // namespace