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 "touch.h" 0022 0023 // STL 0024 #include <cmath> 0025 0026 // Qt 0027 #include <QCoreApplication> 0028 #include <QDateTime> 0029 #include <QGraphicsWidget> 0030 #include <QMouseEvent> 0031 #include <QWidget> 0032 0033 // KF 0034 0035 // Local 0036 #include "gwenview_lib_debug.h" 0037 #include "touch_helper.h" 0038 0039 namespace Gwenview 0040 { 0041 struct TouchPrivate { 0042 Touch *q = nullptr; 0043 QObject *mTarget = nullptr; 0044 Qt::GestureState mLastPanGestureState; 0045 QPointF mLastTapPos; 0046 bool mTabHoldandMovingGestureActive; 0047 qreal mStartZoom; 0048 qreal mZoomModifier; 0049 qreal mRotationThreshold; 0050 qint64 mLastTouchTimeStamp; 0051 0052 TapHoldAndMovingRecognizer *mTapHoldAndMovingRecognizer = nullptr; 0053 Qt::GestureType mTapHoldAndMoving; 0054 TwoFingerPanRecognizer *mTwoFingerPanRecognizer = nullptr; 0055 Qt::GestureType mTwoFingerPan; 0056 OneAndTwoFingerSwipeRecognizer *mOneAndTwoFingerSwipeRecognizer = nullptr; 0057 Qt::GestureType mOneAndTwoFingerSwipe; 0058 DoubleTapRecognizer *mDoubleTapRecognizer = nullptr; 0059 Qt::GestureType mDoubleTap; 0060 TwoFingerTapRecognizer *mTwoFingerTapRecognizer = nullptr; 0061 Qt::GestureType mTwoFingerTap; 0062 }; 0063 0064 Touch::Touch(QObject *target) 0065 : QObject() 0066 , d(new TouchPrivate) 0067 { 0068 d->q = this; 0069 d->mTarget = target; 0070 0071 d->mTapHoldAndMovingRecognizer = new TapHoldAndMovingRecognizer(); 0072 d->mTapHoldAndMoving = QGestureRecognizer::registerRecognizer(d->mTapHoldAndMovingRecognizer); 0073 0074 d->mTwoFingerPanRecognizer = new TwoFingerPanRecognizer(); 0075 d->mTwoFingerPan = QGestureRecognizer::registerRecognizer(d->mTwoFingerPanRecognizer); 0076 0077 d->mTwoFingerTapRecognizer = new TwoFingerTapRecognizer(); 0078 d->mTwoFingerTap = QGestureRecognizer::registerRecognizer(d->mTwoFingerTapRecognizer); 0079 0080 d->mOneAndTwoFingerSwipeRecognizer = new OneAndTwoFingerSwipeRecognizer(); 0081 d->mOneAndTwoFingerSwipe = QGestureRecognizer::registerRecognizer(d->mOneAndTwoFingerSwipeRecognizer); 0082 0083 d->mDoubleTapRecognizer = new DoubleTapRecognizer(); 0084 d->mDoubleTap = QGestureRecognizer::registerRecognizer(d->mDoubleTapRecognizer); 0085 0086 if (qobject_cast<QGraphicsWidget *>(target)) { 0087 auto widgetTarget = qobject_cast<QGraphicsWidget *>(target); 0088 widgetTarget->grabGesture(d->mOneAndTwoFingerSwipe); 0089 widgetTarget->grabGesture(d->mDoubleTap); 0090 widgetTarget->grabGesture(Qt::TapGesture); 0091 widgetTarget->grabGesture(Qt::PinchGesture); 0092 widgetTarget->grabGesture(d->mTwoFingerTap); 0093 widgetTarget->grabGesture(d->mTwoFingerPan); 0094 widgetTarget->grabGesture(d->mTapHoldAndMoving); 0095 } else if (qobject_cast<QWidget *>(target)) { 0096 QWidget *widgetTarget = qobject_cast<QWidget *>(target); 0097 widgetTarget->grabGesture(Qt::TapGesture); 0098 widgetTarget->grabGesture(Qt::PinchGesture); 0099 widgetTarget->grabGesture(d->mTwoFingerTap); 0100 widgetTarget->grabGesture(d->mTwoFingerPan); 0101 widgetTarget->grabGesture(d->mTapHoldAndMoving); 0102 } 0103 target->installEventFilter(this); 0104 } 0105 0106 Touch::~Touch() 0107 { 0108 delete d; 0109 } 0110 0111 bool Touch::eventFilter(QObject *, QEvent *event) 0112 { 0113 if (event->type() == QEvent::TouchBegin) { 0114 d->mLastTouchTimeStamp = QDateTime::currentMSecsSinceEpoch(); 0115 const QPoint pos = Touch_Helper::simpleTouchPosition(event); 0116 touchToMouseMove(pos, event, Qt::NoButton); 0117 return true; 0118 } 0119 if (event->type() == QEvent::TouchUpdate) { 0120 auto touchEvent = static_cast<QTouchEvent *>(event); 0121 d->mLastTouchTimeStamp = QDateTime::currentMSecsSinceEpoch(); 0122 // because we suppress the making of mouse event through Qt, we need to make our own one finger panning 0123 // but only if no TapHoldandMovingGesture is active (Drag and Drop action) 0124 if (touchEvent->touchPoints().size() == 1 && !getTapHoldandMovingGestureActive()) { 0125 const QPointF delta = touchEvent->touchPoints().first().lastPos() - touchEvent->touchPoints().first().pos(); 0126 Q_EMIT PanTriggered(delta); 0127 } 0128 return true; 0129 } 0130 if (event->type() == QEvent::TouchEnd) { 0131 d->mLastTouchTimeStamp = QDateTime::currentMSecsSinceEpoch(); 0132 } 0133 if (event->type() == QEvent::Gesture) { 0134 gestureEvent(static_cast<QGestureEvent *>(event)); 0135 } 0136 return false; 0137 } 0138 0139 bool Touch::gestureEvent(QGestureEvent *event) 0140 { 0141 bool ret = false; 0142 0143 if (checkTwoFingerTapGesture(event)) { 0144 ret = true; 0145 } 0146 0147 if (checkPinchGesture(event)) { 0148 ret = true; 0149 Q_EMIT pinchZoomTriggered(getZoomFromPinchGesture(event), positionGesture(event), d->mLastTouchTimeStamp); 0150 Q_EMIT pinchRotateTriggered(getRotationFromPinchGesture(event)); 0151 } 0152 0153 if (checkTapGesture(event)) { 0154 ret = true; 0155 if (event->widget()) { 0156 touchToMouseClick(positionGesture(event), event->widget()); 0157 } 0158 Q_EMIT tapTriggered(positionGesture(event)); 0159 } 0160 0161 if (checkTapHoldAndMovingGesture(event, d->mTarget)) { 0162 ret = true; 0163 Q_EMIT tapHoldAndMovingTriggered(positionGesture(event)); 0164 } 0165 0166 if (checkDoubleTapGesture(event)) { 0167 ret = true; 0168 } 0169 0170 if (checkOneAndTwoFingerSwipeGesture(event)) { 0171 ret = true; 0172 } 0173 0174 checkTwoFingerPanGesture(event); 0175 0176 return ret; 0177 } 0178 0179 void Touch::setZoomParameter(qreal modifier, qreal startZoom) 0180 { 0181 d->mZoomModifier = modifier; 0182 d->mStartZoom = startZoom; 0183 } 0184 0185 void Touch::setRotationThreshold(qreal rotationThreshold) 0186 { 0187 d->mRotationThreshold = rotationThreshold; 0188 } 0189 0190 qreal Touch::getRotationFromPinchGesture(QGestureEvent *event) 0191 { 0192 const QPinchGesture *pinch = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture)); 0193 static qreal lastRotationAngel; 0194 if (pinch) { 0195 if (pinch->state() == Qt::GestureStarted) { 0196 lastRotationAngel = 0; 0197 return 0.0; 0198 } 0199 if (pinch->state() == Qt::GestureUpdated) { 0200 const qreal rotationDelta = pinch->rotationAngle() - pinch->lastRotationAngle(); 0201 // very low and high changes in the rotation are suspect, so we ignore them 0202 if (abs(rotationDelta) <= 1.5 || abs(rotationDelta) >= 30) { 0203 return 0.0; 0204 } 0205 lastRotationAngel += rotationDelta; 0206 const qreal ret = lastRotationAngel; 0207 if (abs(lastRotationAngel) > d->mRotationThreshold) { 0208 lastRotationAngel = 0; 0209 return ret; 0210 } else { 0211 return 0.0; 0212 } 0213 } 0214 } 0215 return 0.0; 0216 } 0217 0218 qreal Touch::getZoomFromPinchGesture(QGestureEvent *event) 0219 { 0220 static qreal lastZoom; 0221 const QPinchGesture *pinch = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture)); 0222 if (pinch) { 0223 if (pinch->state() == Qt::GestureStarted) { 0224 lastZoom = d->mStartZoom; 0225 return -1; 0226 } 0227 if (pinch->state() == Qt::GestureUpdated) { 0228 lastZoom = calculateZoom(pinch->scaleFactor(), d->mZoomModifier) * lastZoom; 0229 return lastZoom; 0230 } 0231 } 0232 return -1; 0233 } 0234 0235 qreal Touch::calculateZoom(qreal scale, qreal modifier) 0236 { 0237 return ((scale - 1.0) * modifier) + 1.0; 0238 } 0239 0240 QPoint Touch::positionGesture(QGestureEvent *event) 0241 { 0242 // return the position or the center point for follow gestures: QTapGesture, TabHoldAndMovingGesture and PinchGesture; 0243 QPoint position = QPoint(-1, -1); 0244 if (auto tap = static_cast<QTapGesture *>(event->gesture(Qt::TapGesture))) { 0245 position = tap->position().toPoint(); 0246 } else if (QGesture *gesture = event->gesture(getTapHoldandMovingGesture())) { 0247 position = gesture->property("pos").toPoint(); 0248 } else if (auto pinch = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture))) { 0249 if (qobject_cast<QGraphicsWidget *>(d->mTarget)) { 0250 auto widget = qobject_cast<QGraphicsWidget *>(d->mTarget); 0251 position = widget->mapFromScene(event->mapToGraphicsScene(pinch->centerPoint())).toPoint(); 0252 } else { 0253 position = pinch->centerPoint().toPoint(); 0254 } 0255 } 0256 return position; 0257 } 0258 0259 bool Touch::checkTwoFingerPanGesture(QGestureEvent *event) 0260 { 0261 if (QGesture *gesture = event->gesture(getTwoFingerPanGesture())) { 0262 event->accept(); 0263 setPanGestureState(event); 0264 if (gesture->state() == Qt::GestureUpdated) { 0265 const QPoint diff = gesture->property("delta").toPoint(); 0266 Q_EMIT PanTriggered(diff); 0267 return true; 0268 } 0269 } 0270 return false; 0271 } 0272 0273 bool Touch::checkOneAndTwoFingerSwipeGesture(QGestureEvent *event) 0274 { 0275 if (QGesture *gesture = event->gesture(getOneAndTwoFingerSwipeGesture())) { 0276 event->accept(); 0277 if (gesture->state() == Qt::GestureFinished) { 0278 if (gesture->property("right").toBool()) { 0279 Q_EMIT swipeRightTriggered(); 0280 return true; 0281 } else if (gesture->property("left").toBool()) { 0282 Q_EMIT swipeLeftTriggered(); 0283 return true; 0284 } 0285 } 0286 } 0287 return false; 0288 } 0289 0290 bool Touch::checkTapGesture(QGestureEvent *event) 0291 { 0292 const QTapGesture *tap = static_cast<QTapGesture *>(event->gesture(Qt::TapGesture)); 0293 if (tap) { 0294 event->accept(); 0295 if (tap->state() == Qt::GestureFinished) 0296 return true; 0297 } 0298 return false; 0299 } 0300 0301 bool Touch::checkDoubleTapGesture(QGestureEvent *event) 0302 { 0303 if (QGesture *gesture = event->gesture(getDoubleTapGesture())) { 0304 event->accept(); 0305 if (gesture->state() == Qt::GestureFinished) { 0306 Q_EMIT doubleTapTriggered(); 0307 return true; 0308 } 0309 } 0310 return false; 0311 } 0312 0313 bool Touch::checkTwoFingerTapGesture(QGestureEvent *event) 0314 { 0315 if (QGesture *twoFingerTap = event->gesture(getTwoFingerTapGesture())) { 0316 event->accept(); 0317 if (twoFingerTap->state() == Qt::GestureFinished) { 0318 Q_EMIT twoFingerTapTriggered(); 0319 return true; 0320 } 0321 } 0322 return false; 0323 } 0324 0325 bool Touch::checkTapHoldAndMovingGesture(QGestureEvent *event, QObject *target) 0326 { 0327 if (QGesture *tapHoldAndMoving = event->gesture(getTapHoldandMovingGesture())) { 0328 event->accept(); 0329 const QPoint pos = tapHoldAndMoving->property("pos").toPoint(); 0330 switch (tapHoldAndMoving->state()) { 0331 case Qt::GestureStarted: { 0332 setTapHoldandMovingGestureActive(true); 0333 return true; 0334 } 0335 case Qt::GestureUpdated: { 0336 touchToMouseMove(pos, target, Qt::LeftButton); 0337 break; 0338 } 0339 case Qt::GestureCanceled: 0340 case Qt::GestureFinished: { 0341 touchToMouseRelease(pos, target); 0342 setTapHoldandMovingGestureActive(false); 0343 break; 0344 } 0345 default: 0346 break; 0347 } 0348 } 0349 return false; 0350 } 0351 0352 bool Touch::checkPinchGesture(QGestureEvent *event) 0353 { 0354 static qreal lastScaleFactor; 0355 const QPinchGesture *pinch = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture)); 0356 if (pinch) { 0357 // we don't want a pinch gesture, if a pan gesture is active 0358 // only exception is, if the pinch gesture state is Qt::GestureStarted 0359 if (getLastPanGestureState() == Qt::GestureCanceled || pinch->state() == Qt::GestureStarted) { 0360 event->accept(); 0361 if (pinch->state() == Qt::GestureStarted) { 0362 lastScaleFactor = 0; 0363 Q_EMIT pinchGestureStarted(d->mLastTouchTimeStamp); 0364 } else if (pinch->state() == Qt::GestureUpdated) { 0365 // Because of a bug in Qt in a gesture event in a graphicsview, all gestures are trigger twice 0366 // https://bugreports.qt.io/browse/QTBUG-13103 0367 // the duplicate events have the same scaleFactor, so I ignore them 0368 if (lastScaleFactor == pinch->scaleFactor()) { 0369 return false; 0370 } else { 0371 lastScaleFactor = pinch->scaleFactor(); 0372 } 0373 } 0374 return true; 0375 } 0376 } 0377 return false; 0378 } 0379 0380 void Touch::touchToMouseRelease(QPoint pos, QObject *receiver) 0381 { 0382 touchToMouseEvent(pos, receiver, QEvent::MouseButtonRelease, Qt::LeftButton, Qt::LeftButton); 0383 } 0384 0385 void Touch::touchToMouseMove(QPoint pos, QEvent *event, Qt::MouseButton button) 0386 { 0387 if (auto touchEvent = static_cast<QTouchEvent *>(event)) { 0388 touchToMouseEvent(pos, touchEvent->target(), QEvent::MouseMove, button, button); 0389 } 0390 } 0391 0392 void Touch::touchToMouseMove(QPoint pos, QObject *receiver, Qt::MouseButton button) 0393 { 0394 touchToMouseEvent(pos, receiver, QEvent::MouseMove, button, button); 0395 } 0396 0397 void Touch::touchToMouseClick(QPoint pos, QObject *receiver) 0398 { 0399 touchToMouseEvent(pos, receiver, QEvent::MouseButtonPress, Qt::LeftButton, Qt::LeftButton); 0400 touchToMouseEvent(pos, receiver, QEvent::MouseButtonRelease, Qt::LeftButton, Qt::LeftButton); 0401 } 0402 0403 void Touch::touchToMouseEvent(QPoint pos, QObject *receiver, QEvent::Type type, Qt::MouseButton button, Qt::MouseButtons buttons) 0404 { 0405 auto evt = new QMouseEvent(type, pos, button, buttons, Qt::NoModifier); 0406 QCoreApplication::postEvent(receiver, evt); 0407 } 0408 0409 Qt::GestureState Touch::getLastPanGestureState() 0410 { 0411 return d->mLastPanGestureState; 0412 ; 0413 } 0414 0415 void Touch::setPanGestureState(QGestureEvent *event) 0416 { 0417 if (QGesture *panGesture = event->gesture(getTwoFingerPanGesture())) { 0418 d->mLastPanGestureState = panGesture->state(); 0419 } 0420 return; 0421 } 0422 0423 QPointF Touch::getLastTapPos() 0424 { 0425 return d->mLastTapPos; 0426 } 0427 0428 void Touch::setLastTapPos(QPointF pos) 0429 { 0430 d->mLastTapPos = pos; 0431 } 0432 0433 Qt::GestureType Touch::getTapHoldandMovingGesture() 0434 { 0435 return d->mTapHoldAndMoving; 0436 } 0437 0438 Qt::GestureType Touch::getTwoFingerPanGesture() 0439 { 0440 return d->mTwoFingerPan; 0441 } 0442 0443 Qt::GestureType Touch::getOneAndTwoFingerSwipeGesture() 0444 { 0445 return d->mOneAndTwoFingerSwipe; 0446 } 0447 0448 Qt::GestureType Touch::getDoubleTapGesture() 0449 { 0450 return d->mDoubleTap; 0451 } 0452 0453 Qt::GestureType Touch::getTwoFingerTapGesture() 0454 { 0455 return d->mTwoFingerTap; 0456 } 0457 0458 bool Touch::getTapHoldandMovingGestureActive() 0459 { 0460 return d->mTabHoldandMovingGestureActive; 0461 } 0462 0463 void Touch::setTapHoldandMovingGestureActive(bool active) 0464 { 0465 d->mTabHoldandMovingGestureActive = active; 0466 } 0467 0468 } // namespace 0469 0470 #include "moc_touch.cpp"