1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qgesture.h"
41#include "qapplication.h"
42#include "qevent.h"
43#include "qwidget.h"
44#if QT_CONFIG(graphicsview)
45#include "qgraphicsitem.h"
46#include "qgraphicsscene.h"
47#include "qgraphicssceneevent.h"
48#include "qgraphicsview.h"
49#endif
50#include "qscroller.h"
51#include <QtGui/qtouchdevice.h>
52#include "private/qapplication_p.h"
53#include "private/qevent_p.h"
54#include "private/qflickgesture_p.h"
55#include "qdebug.h"
56
57#ifndef QT_NO_GESTURES
58
59QT_BEGIN_NAMESPACE
60
61//#define QFLICKGESTURE_DEBUG
62
63#ifdef QFLICKGESTURE_DEBUG
64# define qFGDebug qDebug
65#else
66# define qFGDebug while (false) qDebug
67#endif
68
69extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
70
71static QMouseEvent *copyMouseEvent(QEvent *e)
72{
73 switch (e->type()) {
74 case QEvent::MouseButtonPress:
75 case QEvent::MouseButtonRelease:
76 case QEvent::MouseMove: {
77 QMouseEvent *me = static_cast<QMouseEvent *>(e);
78 QMouseEvent *cme = new QMouseEvent(me->type(), QPoint(0, 0), me->windowPos(), me->screenPos(),
79 me->button(), me->buttons(), me->modifiers(), me->source());
80 return cme;
81 }
82#if QT_CONFIG(graphicsview)
83 case QEvent::GraphicsSceneMousePress:
84 case QEvent::GraphicsSceneMouseRelease:
85 case QEvent::GraphicsSceneMouseMove: {
86 QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(e);
87#if 1
88 QEvent::Type met = me->type() == QEvent::GraphicsSceneMousePress ? QEvent::MouseButtonPress :
89 (me->type() == QEvent::GraphicsSceneMouseRelease ? QEvent::MouseButtonRelease : QEvent::MouseMove);
90 QMouseEvent *cme = new QMouseEvent(met, QPoint(0, 0), QPoint(0, 0), me->screenPos(),
91 me->button(), me->buttons(), me->modifiers(), me->source());
92 return cme;
93#else
94 QGraphicsSceneMouseEvent *copy = new QGraphicsSceneMouseEvent(me->type());
95 copy->setPos(me->pos());
96 copy->setScenePos(me->scenePos());
97 copy->setScreenPos(me->screenPos());
98 for (int i = 0x1; i <= 0x10; i <<= 1) {
99 Qt::MouseButton button = Qt::MouseButton(i);
100 copy->setButtonDownPos(button, me->buttonDownPos(button));
101 copy->setButtonDownScenePos(button, me->buttonDownScenePos(button));
102 copy->setButtonDownScreenPos(button, me->buttonDownScreenPos(button));
103 }
104 copy->setLastPos(me->lastPos());
105 copy->setLastScenePos(me->lastScenePos());
106 copy->setLastScreenPos(me->lastScreenPos());
107 copy->setButtons(me->buttons());
108 copy->setButton(me->button());
109 copy->setModifiers(me->modifiers());
110 copy->setSource(me->source());
111 copy->setFlags(me->flags());
112 return copy;
113#endif
114 }
115#endif // QT_CONFIG(graphicsview)
116 default:
117 return nullptr;
118 }
119}
120
121class PressDelayHandler : public QObject
122{
123private:
124 PressDelayHandler(QObject *parent = nullptr)
125 : QObject(parent)
126 , pressDelayTimer(0)
127 , sendingEvent(false)
128 , mouseButton(Qt::NoButton)
129 , mouseTarget(nullptr)
130 , mouseEventSource(Qt::MouseEventNotSynthesized)
131 { }
132
133public:
134 enum {
135 UngrabMouseBefore = 1,
136 RegrabMouseAfterwards = 2
137 };
138
139 static PressDelayHandler *instance()
140 {
141 static PressDelayHandler *inst = nullptr;
142 if (!inst)
143 inst = new PressDelayHandler(QCoreApplication::instance());
144 return inst;
145 }
146
147 bool shouldEventBeIgnored(QEvent *) const
148 {
149 return sendingEvent;
150 }
151
152 bool isDelaying() const
153 {
154 return !pressDelayEvent.isNull();
155 }
156
157 void pressed(QEvent *e, int delay)
158 {
159 if (!pressDelayEvent) {
160 pressDelayEvent.reset(other: copyMouseEvent(e));
161 pressDelayTimer = startTimer(interval: delay);
162 mouseTarget = QApplication::widgetAt(p: pressDelayEvent->globalPos());
163 mouseButton = pressDelayEvent->button();
164 mouseEventSource = pressDelayEvent->source();
165 qFGDebug(msg: "QFG: consuming/delaying mouse press");
166 } else {
167 qFGDebug(msg: "QFG: NOT consuming/delaying mouse press");
168 }
169 e->setAccepted(true);
170 }
171
172 bool released(QEvent *e, bool scrollerWasActive, bool scrollerIsActive)
173 {
174 // consume this event if the scroller was or is active
175 bool result = scrollerWasActive || scrollerIsActive;
176
177 // stop the timer
178 if (pressDelayTimer) {
179 killTimer(id: pressDelayTimer);
180 pressDelayTimer = 0;
181 }
182 // we still haven't even sent the press, so do it now
183 if (pressDelayEvent && mouseTarget && !scrollerIsActive) {
184 QScopedPointer<QMouseEvent> releaseEvent(copyMouseEvent(e));
185
186 qFGDebug() << "QFG: re-sending mouse press (due to release) for " << mouseTarget;
187 sendMouseEvent(me: pressDelayEvent.data(), flags: UngrabMouseBefore);
188
189 qFGDebug() << "QFG: faking mouse release (due to release) for " << mouseTarget;
190 sendMouseEvent(me: releaseEvent.data());
191
192 result = true; // consume this event
193 } else if (mouseTarget && scrollerIsActive) {
194 // we grabbed the mouse expicitly when the scroller became active, so undo that now
195 sendMouseEvent(me: nullptr, flags: UngrabMouseBefore);
196 }
197 pressDelayEvent.reset(other: nullptr);
198 mouseTarget = nullptr;
199 return result;
200 }
201
202 void scrollerWasIntercepted()
203 {
204 qFGDebug(msg: "QFG: deleting delayed mouse press, since scroller was only intercepted");
205 if (pressDelayEvent) {
206 // we still haven't even sent the press, so just throw it away now
207 if (pressDelayTimer) {
208 killTimer(id: pressDelayTimer);
209 pressDelayTimer = 0;
210 }
211 pressDelayEvent.reset(other: nullptr);
212 }
213 mouseTarget = nullptr;
214 }
215
216 void scrollerBecameActive()
217 {
218 if (pressDelayEvent) {
219 // we still haven't even sent the press, so just throw it away now
220 qFGDebug(msg: "QFG: deleting delayed mouse press, since scroller is active now");
221 if (pressDelayTimer) {
222 killTimer(id: pressDelayTimer);
223 pressDelayTimer = 0;
224 }
225 pressDelayEvent.reset(other: nullptr);
226 mouseTarget = nullptr;
227 } else if (mouseTarget) {
228 // we did send a press, so we need to fake a release now
229
230 // release all pressed mouse buttons
231 /* Qt::MouseButtons mouseButtons = QGuiApplication::mouseButtons();
232 for (int i = 0; i < 32; ++i) {
233 if (mouseButtons & (1 << i)) {
234 Qt::MouseButton b = static_cast<Qt::MouseButton>(1 << i);
235 mouseButtons &= ~b;
236 QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
237
238 qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget;
239 QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway,
240 b, mouseButtons, QGuiApplication::keyboardModifiers());
241 sendMouseEvent(&re);
242 }
243 }*/
244
245 QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
246
247 qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget;
248 QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway, farFarAway,
249 mouseButton, QGuiApplication::mouseButtons() & ~mouseButton,
250 QGuiApplication::keyboardModifiers(), mouseEventSource);
251 sendMouseEvent(me: &re, flags: RegrabMouseAfterwards);
252 // don't clear the mouseTarget just yet, since we need to explicitly ungrab the mouse on release!
253 }
254 }
255
256protected:
257 void timerEvent(QTimerEvent *e) override
258 {
259 if (e->timerId() == pressDelayTimer) {
260 if (pressDelayEvent && mouseTarget) {
261 qFGDebug() << "QFG: timer event: re-sending mouse press to " << mouseTarget;
262 sendMouseEvent(me: pressDelayEvent.data(), flags: UngrabMouseBefore);
263 }
264 pressDelayEvent.reset(other: nullptr);
265
266 if (pressDelayTimer) {
267 killTimer(id: pressDelayTimer);
268 pressDelayTimer = 0;
269 }
270 }
271 }
272
273 void sendMouseEvent(QMouseEvent *me, int flags = 0)
274 {
275 if (mouseTarget) {
276 sendingEvent = true;
277
278#if QT_CONFIG(graphicsview)
279 QGraphicsItem *grabber = nullptr;
280 if (mouseTarget->parentWidget()) {
281 if (QGraphicsView *gv = qobject_cast<QGraphicsView *>(object: mouseTarget->parentWidget())) {
282 if (gv->scene())
283 grabber = gv->scene()->mouseGrabberItem();
284 }
285 }
286
287 if (grabber && (flags & UngrabMouseBefore)) {
288 // GraphicsView Mouse Handling Workaround #1:
289 // we need to ungrab the mouse before re-sending the press,
290 // since the scene had already set the mouse grabber to the
291 // original (and consumed) event's receiver
292 qFGDebug() << "QFG: ungrabbing" << grabber;
293 grabber->ungrabMouse();
294 }
295#else
296 Q_UNUSED(flags);
297#endif // QT_CONFIG(graphicsview)
298
299 if (me) {
300 QMouseEvent copy(me->type(), mouseTarget->mapFromGlobal(me->globalPos()),
301 mouseTarget->topLevelWidget()->mapFromGlobal(me->globalPos()), me->screenPos(),
302 me->button(), me->buttons(), me->modifiers(), me->source());
303 qt_sendSpontaneousEvent(receiver: mouseTarget, event: &copy);
304 }
305
306#if QT_CONFIG(graphicsview)
307 if (grabber && (flags & RegrabMouseAfterwards)) {
308 // GraphicsView Mouse Handling Workaround #2:
309 // we need to re-grab the mouse after sending a faked mouse
310 // release, since we still need the mouse moves for the gesture
311 // (the scene will clear the item's mouse grabber status on
312 // release).
313 qFGDebug() << "QFG: re-grabbing" << grabber;
314 grabber->grabMouse();
315 }
316#endif
317 sendingEvent = false;
318 }
319 }
320
321
322private:
323 int pressDelayTimer;
324 QScopedPointer<QMouseEvent> pressDelayEvent;
325 bool sendingEvent;
326 Qt::MouseButton mouseButton;
327 QPointer<QWidget> mouseTarget;
328 Qt::MouseEventSource mouseEventSource;
329};
330
331
332/*!
333 \internal
334 \class QFlickGesture
335 \since 4.8
336 \brief The QFlickGesture class describes a flicking gesture made by the user.
337 \ingroup gestures
338 The QFlickGesture is more complex than the QPanGesture that uses QScroller and QScrollerProperties
339 to decide if it is triggered.
340 This gesture is reacting on touch event as compared to the QMouseFlickGesture.
341
342 \sa {Gestures in Widgets and Graphics View}, QScroller, QScrollerProperties, QMouseFlickGesture
343*/
344
345/*!
346 \internal
347*/
348QFlickGesture::QFlickGesture(QObject *receiver, Qt::MouseButton button, QObject *parent)
349 : QGesture(*new QFlickGesturePrivate, parent)
350{
351 d_func()->q_ptr = this;
352 d_func()->receiver = receiver;
353 d_func()->receiverScroller = (receiver && QScroller::hasScroller(target: receiver)) ? QScroller::scroller(target: receiver) : nullptr;
354 d_func()->button = button;
355}
356
357QFlickGesture::~QFlickGesture()
358{ }
359
360QFlickGesturePrivate::QFlickGesturePrivate()
361 : receiverScroller(nullptr), button(Qt::NoButton), macIgnoreWheel(false)
362{ }
363
364
365//
366// QFlickGestureRecognizer
367//
368
369
370QFlickGestureRecognizer::QFlickGestureRecognizer(Qt::MouseButton button)
371{
372 this->button = button;
373}
374
375/*! \reimp
376 */
377QGesture *QFlickGestureRecognizer::create(QObject *target)
378{
379#if QT_CONFIG(graphicsview)
380 QGraphicsObject *go = qobject_cast<QGraphicsObject*>(object: target);
381 if (go && button == Qt::NoButton) {
382 go->setAcceptTouchEvents(true);
383 }
384#endif
385 return new QFlickGesture(target, button);
386}
387
388/*! \internal
389 The recognize function detects a touch event suitable to start the attached QScroller.
390 The QFlickGesture will be triggered as soon as the scroller is no longer in the state
391 QScroller::Inactive or QScroller::Pressed. It will be finished or canceled
392 at the next QEvent::TouchEnd.
393 Note that the QScroller might continue scrolling (kinetically) at this point.
394 */
395QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
396 QObject *watched,
397 QEvent *event)
398{
399 Q_UNUSED(watched);
400
401 static QElapsedTimer monotonicTimer;
402 if (!monotonicTimer.isValid())
403 monotonicTimer.start();
404
405 QFlickGesture *q = static_cast<QFlickGesture *>(state);
406 QFlickGesturePrivate *d = q->d_func();
407
408 QScroller *scroller = d->receiverScroller;
409 if (!scroller)
410 return Ignore; // nothing to do without a scroller?
411
412 QWidget *receiverWidget = qobject_cast<QWidget *>(o: d->receiver);
413#if QT_CONFIG(graphicsview)
414 QGraphicsObject *receiverGraphicsObject = qobject_cast<QGraphicsObject *>(object: d->receiver);
415#endif
416
417 // this is only set for events that we inject into the event loop via sendEvent()
418 if (PressDelayHandler::instance()->shouldEventBeIgnored(event)) {
419 //qFGDebug() << state << "QFG: ignored event: " << event->type();
420 return Ignore;
421 }
422
423 const QMouseEvent *me = nullptr;
424#if QT_CONFIG(graphicsview)
425 const QGraphicsSceneMouseEvent *gsme = nullptr;
426#endif
427 const QTouchEvent *te = nullptr;
428 QPoint globalPos;
429
430 // qFGDebug() << "FlickGesture "<<state<<"watched:"<<watched<<"receiver"<<d->receiver<<"event"<<event->type()<<"button"<<button;
431
432 switch (event->type()) {
433 case QEvent::MouseButtonPress:
434 case QEvent::MouseButtonRelease:
435 case QEvent::MouseMove:
436 if (!receiverWidget)
437 return Ignore;
438 if (button != Qt::NoButton) {
439 me = static_cast<const QMouseEvent *>(event);
440 globalPos = me->globalPos();
441 }
442 break;
443#if QT_CONFIG(graphicsview)
444 case QEvent::GraphicsSceneMousePress:
445 case QEvent::GraphicsSceneMouseRelease:
446 case QEvent::GraphicsSceneMouseMove:
447 if (!receiverGraphicsObject)
448 return Ignore;
449 if (button != Qt::NoButton) {
450 gsme = static_cast<const QGraphicsSceneMouseEvent *>(event);
451 globalPos = gsme->screenPos();
452 }
453 break;
454#endif
455 case QEvent::TouchBegin:
456 case QEvent::TouchEnd:
457 case QEvent::TouchUpdate:
458 if (button == Qt::NoButton) {
459 te = static_cast<const QTouchEvent *>(event);
460 if (!te->touchPoints().isEmpty())
461 globalPos = te->touchPoints().at(i: 0).screenPos().toPoint();
462 }
463 break;
464
465 // consume all wheel events if the scroller is active
466 case QEvent::Wheel:
467 if (d->macIgnoreWheel || (scroller->state() != QScroller::Inactive))
468 return Ignore | ConsumeEventHint;
469 break;
470
471 // consume all dbl click events if the scroller is active
472 case QEvent::MouseButtonDblClick:
473 if (scroller->state() != QScroller::Inactive)
474 return Ignore | ConsumeEventHint;
475 break;
476
477 default:
478 break;
479 }
480
481 if (!me
482#if QT_CONFIG(graphicsview)
483 && !gsme
484#endif
485 && !te) // Neither mouse nor touch
486 return Ignore;
487
488 // get the current pointer position in local coordinates.
489 QPointF point;
490 QScroller::Input inputType = (QScroller::Input) 0;
491
492 switch (event->type()) {
493 case QEvent::MouseButtonPress:
494 if (me && me->button() == button && me->buttons() == button) {
495 point = me->globalPos();
496 inputType = QScroller::InputPress;
497 } else if (me) {
498 scroller->stop();
499 return CancelGesture;
500 }
501 break;
502 case QEvent::MouseButtonRelease:
503 if (me && me->button() == button) {
504 point = me->globalPos();
505 inputType = QScroller::InputRelease;
506 }
507 break;
508 case QEvent::MouseMove:
509 if (me && me->buttons() == button) {
510 point = me->globalPos();
511 inputType = QScroller::InputMove;
512 }
513 break;
514
515#if QT_CONFIG(graphicsview)
516 case QEvent::GraphicsSceneMousePress:
517 if (gsme && gsme->button() == button && gsme->buttons() == button) {
518 point = gsme->scenePos();
519 inputType = QScroller::InputPress;
520 } else if (gsme) {
521 scroller->stop();
522 return CancelGesture;
523 }
524 break;
525 case QEvent::GraphicsSceneMouseRelease:
526 if (gsme && gsme->button() == button) {
527 point = gsme->scenePos();
528 inputType = QScroller::InputRelease;
529 }
530 break;
531 case QEvent::GraphicsSceneMouseMove:
532 if (gsme && gsme->buttons() == button) {
533 point = gsme->scenePos();
534 inputType = QScroller::InputMove;
535 }
536 break;
537#endif
538
539 case QEvent::TouchBegin:
540 inputType = QScroller::InputPress;
541 Q_FALLTHROUGH();
542 case QEvent::TouchEnd:
543 if (!inputType)
544 inputType = QScroller::InputRelease;
545 Q_FALLTHROUGH();
546 case QEvent::TouchUpdate:
547 if (!inputType)
548 inputType = QScroller::InputMove;
549
550 if (te->device()->type() == QTouchDevice::TouchPad) {
551 if (te->touchPoints().count() != 2) // 2 fingers on pad
552 return Ignore;
553
554 point = te->touchPoints().at(i: 0).startScenePos() +
555 ((te->touchPoints().at(i: 0).scenePos() - te->touchPoints().at(i: 0).startScenePos()) +
556 (te->touchPoints().at(i: 1).scenePos() - te->touchPoints().at(i: 1).startScenePos())) / 2;
557 } else { // TouchScreen
558 if (te->touchPoints().count() != 1) // 1 finger on screen
559 return Ignore;
560
561 point = te->touchPoints().at(i: 0).scenePos();
562 }
563 break;
564
565 default:
566 break;
567 }
568
569 // Check for an active scroller at globalPos
570 if (inputType == QScroller::InputPress) {
571 const auto activeScrollers = QScroller::activeScrollers();
572 for (QScroller *as : activeScrollers) {
573 if (as != scroller) {
574 QRegion scrollerRegion;
575
576 if (QWidget *w = qobject_cast<QWidget *>(o: as->target())) {
577 scrollerRegion = QRect(w->mapToGlobal(QPoint(0, 0)), w->size());
578#if QT_CONFIG(graphicsview)
579 } else if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(object: as->target())) {
580 if (const auto *scene = go->scene()) {
581 const auto goBoundingRectMappedToScene = go->mapToScene(rect: go->boundingRect());
582 const auto views = scene->views();
583 for (QGraphicsView *gv : views) {
584 scrollerRegion |= gv->mapFromScene(polygon: goBoundingRectMappedToScene)
585 .translated(offset: gv->mapToGlobal(QPoint(0, 0)));
586 }
587 }
588#endif
589 }
590 // active scrollers always have priority
591 if (scrollerRegion.contains(p: globalPos))
592 return Ignore;
593 }
594 }
595 }
596
597 bool scrollerWasDragging = (scroller->state() == QScroller::Dragging);
598 bool scrollerWasScrolling = (scroller->state() == QScroller::Scrolling);
599
600 if (inputType) {
601 if (QWidget *w = qobject_cast<QWidget *>(o: d->receiver))
602 point = w->mapFromGlobal(point.toPoint());
603#if QT_CONFIG(graphicsview)
604 else if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(object: d->receiver))
605 point = go->mapFromScene(point);
606#endif
607
608 // inform the scroller about the new event
609 scroller->handleInput(input: inputType, position: point, timestamp: monotonicTimer.elapsed());
610 }
611
612 // depending on the scroller state return the gesture state
613 Result result;
614 bool scrollerIsActive = (scroller->state() == QScroller::Dragging ||
615 scroller->state() == QScroller::Scrolling);
616
617 // Consume all mouse events while dragging or scrolling to avoid nasty
618 // side effects with Qt's standard widgets.
619 if ((me
620#if QT_CONFIG(graphicsview)
621 || gsme
622#endif
623 ) && scrollerIsActive)
624 result |= ConsumeEventHint;
625
626 // The only problem with this approach is that we consume the
627 // MouseRelease when we start the scrolling with a flick gesture, so we
628 // have to fake a MouseRelease "somewhere" to not mess with the internal
629 // states of Qt's widgets (a QPushButton would stay in 'pressed' state
630 // forever, if it doesn't receive a MouseRelease).
631 if (me
632#if QT_CONFIG(graphicsview)
633 || gsme
634#endif
635 ) {
636 if (!scrollerWasDragging && !scrollerWasScrolling && scrollerIsActive)
637 PressDelayHandler::instance()->scrollerBecameActive();
638 else if (scrollerWasScrolling && (scroller->state() == QScroller::Dragging || scroller->state() == QScroller::Inactive))
639 PressDelayHandler::instance()->scrollerWasIntercepted();
640 }
641
642 if (!inputType) {
643 result |= Ignore;
644 } else {
645 switch (event->type()) {
646 case QEvent::MouseButtonPress:
647#if QT_CONFIG(graphicsview)
648 case QEvent::GraphicsSceneMousePress:
649#endif
650 if (scroller->state() == QScroller::Pressed) {
651 int pressDelay = int(1000 * scroller->scrollerProperties().scrollMetric(metric: QScrollerProperties::MousePressEventDelay).toReal());
652 if (pressDelay > 0) {
653 result |= ConsumeEventHint;
654
655 PressDelayHandler::instance()->pressed(e: event, delay: pressDelay);
656 event->accept();
657 }
658 }
659 Q_FALLTHROUGH();
660 case QEvent::TouchBegin:
661 q->setHotSpot(globalPos);
662 result |= scrollerIsActive ? TriggerGesture : MayBeGesture;
663 break;
664
665 case QEvent::MouseMove:
666#if QT_CONFIG(graphicsview)
667 case QEvent::GraphicsSceneMouseMove:
668#endif
669 if (PressDelayHandler::instance()->isDelaying())
670 result |= ConsumeEventHint;
671 Q_FALLTHROUGH();
672 case QEvent::TouchUpdate:
673 result |= scrollerIsActive ? TriggerGesture : Ignore;
674 break;
675
676#if QT_CONFIG(graphicsview)
677 case QEvent::GraphicsSceneMouseRelease:
678#endif
679 case QEvent::MouseButtonRelease:
680 if (PressDelayHandler::instance()->released(e: event, scrollerWasActive: scrollerWasDragging || scrollerWasScrolling, scrollerIsActive))
681 result |= ConsumeEventHint;
682 Q_FALLTHROUGH();
683 case QEvent::TouchEnd:
684 result |= scrollerIsActive ? FinishGesture : CancelGesture;
685 break;
686
687 default:
688 result |= Ignore;
689 break;
690 }
691 }
692 return result;
693}
694
695
696/*! \reimp
697 */
698void QFlickGestureRecognizer::reset(QGesture *state)
699{
700 QGestureRecognizer::reset(state);
701}
702
703QT_END_NAMESPACE
704
705#include "moc_qflickgesture_p.cpp"
706
707#endif // QT_NO_GESTURES
708

source code of qtbase/src/widgets/util/qflickgesture.cpp