7#include <QtQuick/private/qquickdeliveryagent_p_p.h>
8#include <QtQuick/qquickwindow.h>
9#include <qpa/qplatformtheme.h>
10#include <private/qguiapplication_p.h>
11#include <QtGui/qstylehints.h>
15Q_STATIC_LOGGING_CATEGORY(lcTapHandler,
"qt.quick.handler.tap")
17quint64 QQuickTapHandler::m_multiTapInterval(0);
19int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1);
20int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
57QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
58 : QQuickSinglePointHandler(parent)
59 , m_longPressThreshold(QGuiApplication::styleHints()->mousePressAndHoldInterval())
61 if (m_mouseMultiClickDistanceSquared < 0) {
62 m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval();
63 m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance();
64 m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
65 m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance();
66 m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared;
70bool QQuickTapHandler::wantsEventPoint(
const QPointerEvent *event,
const QEventPoint &point)
72 if (!QQuickDeliveryAgentPrivate::isMouseEvent(event) &&
73 !QQuickDeliveryAgentPrivate::isTouchEvent(event) &&
74 !QQuickDeliveryAgentPrivate::isTabletEvent(event))
81 bool overThreshold = d_func()->dragOverThreshold(point);
82 if (overThreshold && m_gesturePolicy != DragWithinBounds) {
83 if (m_longPressTimer.isActive())
84 qCDebug(lcTapHandler) << objectName() <<
"drag threshold exceeded";
85 m_longPressTimer.stop();
86 m_holdTimer.invalidate();
88 switch (point.state()) {
89 case QEventPoint::Pressed:
90 case QEventPoint::Released:
91 ret = parentContains(point);
93 case QEventPoint::Updated:
94 ret = point.id() ==
this->point().id();
95 switch (m_gesturePolicy) {
97 ret = ret && !overThreshold && parentContains(point);
100 case DragWithinBounds:
101 ret = ret && parentContains(point);
103 case ReleaseWithinBounds:
108 case QEventPoint::Stationary:
111 ret = point.id() ==
this->point().id();
113 case QEventPoint::Unknown:
120 if (!ret && point.id() ==
this->point().id())
121 setPressed(
false,
true,
const_cast<QPointerEvent *>(event),
const_cast<QEventPoint &>(point));
125void QQuickTapHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point)
127 const bool isTouch = QQuickDeliveryAgentPrivate::isTouchEvent(event);
128 switch (point.state()) {
129 case QEventPoint::Pressed:
130 setPressed(
true,
false, event, point);
132 case QEventPoint::Released: {
133 if (isTouch || (
static_cast<
const QSinglePointEvent *>(event)->buttons() & acceptedButtons()) == Qt::NoButton)
134 setPressed(
false,
false, event, point);
141 QQuickSinglePointHandler::handleEventPoint(event, point);
147 if (isTouch && m_gesturePolicy == DragThreshold)
148 point.setAccepted(
false);
152
153
154
155
156
157
158
159
160
161
162
163
164
165qreal QQuickTapHandler::longPressThreshold()
const
167 return m_longPressThreshold / qreal(1000);
170void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
172 if (longPressThreshold < 0) {
173 resetLongPressThreshold();
176 int ms = qRound(longPressThreshold * 1000);
177 if (m_longPressThreshold == ms)
180 m_longPressThreshold = ms;
181 emit longPressThresholdChanged();
184void QQuickTapHandler::resetLongPressThreshold()
186 int ms = QGuiApplication::styleHints()->mousePressAndHoldInterval();
187 if (m_longPressThreshold == ms)
190 m_longPressThreshold = ms;
191 emit longPressThresholdChanged();
194void QQuickTapHandler::timerEvent(QTimerEvent *event)
196 if (event->timerId() == m_longPressTimer.timerId()) {
197 m_longPressTimer.stop();
198 qCDebug(lcTapHandler) << objectName() <<
"longPressed";
199 m_longPressed =
true;
201 }
else if (event->timerId() == m_doubleTapTimer.timerId()) {
202 m_doubleTapTimer.stop();
203 qCDebug(lcTapHandler) << objectName() <<
"double-tap timer expired; taps:" << m_tapCount;
204 Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap));
206 emit singleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
207 else if (m_tapCount == 2)
208 emit doubleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
321 if (m_gesturePolicy == gesturePolicy)
324 m_gesturePolicy = gesturePolicy;
325 emit gesturePolicyChanged();
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc)
353 if (m_exclusiveSignals == exc)
356 m_exclusiveSignals = exc;
357 emit exclusiveSignalsChanged();
361
362
363
364
365
366
367
368
369void QQuickTapHandler::setPressed(
bool press,
bool cancel, QPointerEvent *event, QEventPoint &point)
371 if (m_pressed != press) {
372 qCDebug(lcTapHandler) << objectName() <<
"pressed" << m_pressed <<
"->" << press
373 << (cancel ?
"CANCEL" :
"") << point <<
"gp" << m_gesturePolicy;
375 connectPreRenderSignal(press);
378 if (m_longPressThreshold > 0)
379 m_longPressTimer.start(m_longPressThreshold,
this);
382 m_longPressTimer.stop();
383 m_holdTimer.invalidate();
387 if (m_gesturePolicy == DragThreshold)
388 setPassiveGrab(event, point, press);
390 setExclusiveGrab(event, point, press);
392 if (!cancel && !press && parentContains(point)) {
394 qCDebug(lcTapHandler) << objectName() <<
"long press threshold" << longPressThreshold() <<
"exceeded:" << point.timeHeld();
397 const quint64 ts = event->timestamp();
398 const quint64 interval = ts - m_lastTapTimestamp;
399 const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
400 const auto singleTapReleasedButton = event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
401 if ((interval < m_multiTapInterval && distanceSquared <
402 (event->device()->type() == QInputDevice::DeviceType::Mouse ?
403 m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
404 && m_singleTapReleasedButton == singleTapReleasedButton) {
407 m_singleTapReleasedButton = singleTapReleasedButton;
408 m_singleTapReleasedPoint = point;
411 qCDebug(lcTapHandler) << objectName() <<
"tapped" << m_tapCount <<
"times; interval since last:" << interval
412 <<
"sec; distance since last:" << qSqrt(distanceSquared);
413 auto button = event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
414 emit tapped(point, button);
415 emit tapCountChanged();
416 switch (m_exclusiveSignals) {
419 emit singleTapped(point, button);
420 else if (m_tapCount == 2)
421 emit doubleTapped(point, button);
425 emit singleTapped(point, button);
429 emit doubleTapped(point, button);
431 case (SingleTap | DoubleTap):
432 if (m_tapCount == 1) {
433 qCDebug(lcTapHandler) << objectName() <<
"waiting to emit singleTapped:" << m_multiTapInterval <<
"ms";
434 m_doubleTapTimer.start(m_multiTapInterval,
this);
437 qCDebug(lcTapHandler) << objectName() <<
"tap" << m_tapCount <<
"after" << event->timestamp() - m_lastTapTimestamp <<
"ms";
439 m_lastTapTimestamp = ts;
440 m_lastTapPos = point.scenePosition();
443 m_longPressed =
false;
444 emit pressedChanged();
445 if (!press && m_gesturePolicy != DragThreshold) {
447 setExclusiveGrab(event, point, press);
450 emit canceled(point);
452 setExclusiveGrab(event, point,
false);
461void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition,
462 QPointerEvent *ev, QEventPoint &point)
465 QQuickSinglePointHandler::onGrabChanged(grabber, transition, ev, point);
468 const bool isCanceled = transition == QPointingDevice::CancelGrabExclusive || transition == QPointingDevice::CancelGrabPassive;
471 const bool passiveGrab = transition == QPointingDevice::GrabPassive || transition == QPointingDevice::UngrabPassive;
472 if (grabber ==
this && (isCanceled || point.state() == QEventPoint::Released || (!active() && !passiveGrab)))
473 setPressed(
false, isCanceled, ev, point);
476void QQuickTapHandler::connectPreRenderSignal(
bool conn)
479 disconnect(m_preRenderSignalConnection);
481 auto par = parentItem();
482 if (!par || !par->window())
486
487
488
489
490
491
492
494 m_preRenderSignalConnection = connect(par->window(), &QQuickWindow::beforeSynchronizing,
495 this, &QQuickTapHandler::updateTimeHeld);
499void QQuickTapHandler::updateTimeHeld()
501 emit timeHeldChanged();
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
545
546
547
548
549
550
551
552
553
554
555
556
557
558
561
562
563
564
565
566
567
568
569
570
571
574
575
576
577
578
579
580
581
582
583
584
585
586
589
590
591
592
593
594
595
596
599
600
601
602
603
604
607#include "moc_qquicktaphandler_p.cpp"
Combined button and popup list for selecting options.