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);
145 if (isTouch && m_gesturePolicy == DragThreshold)
146 point.setAccepted(
false);
150
151
152
153
154
155
156
157
158
159
160
161
162
163qreal QQuickTapHandler::longPressThreshold()
const
165 return m_longPressThreshold / qreal(1000);
168void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
170 if (longPressThreshold < 0) {
171 resetLongPressThreshold();
174 int ms = qRound(longPressThreshold * 1000);
175 if (m_longPressThreshold == ms)
178 m_longPressThreshold = ms;
179 emit longPressThresholdChanged();
182void QQuickTapHandler::resetLongPressThreshold()
184 int ms = QGuiApplication::styleHints()->mousePressAndHoldInterval();
185 if (m_longPressThreshold == ms)
188 m_longPressThreshold = ms;
189 emit longPressThresholdChanged();
192void QQuickTapHandler::timerEvent(QTimerEvent *event)
194 if (event->timerId() == m_longPressTimer.timerId()) {
195 m_longPressTimer.stop();
196 qCDebug(lcTapHandler) << objectName() <<
"longPressed";
197 m_longPressed =
true;
199 }
else if (event->timerId() == m_doubleTapTimer.timerId()) {
200 m_doubleTapTimer.stop();
201 qCDebug(lcTapHandler) << objectName() <<
"double-tap timer expired; taps:" << m_tapCount;
202 Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap));
204 emit singleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
205 else if (m_tapCount == 2)
206 emit doubleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
211
212
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
313void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
315 if (m_gesturePolicy == gesturePolicy)
318 m_gesturePolicy = gesturePolicy;
319 emit gesturePolicyChanged();
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc)
347 if (m_exclusiveSignals == exc)
350 m_exclusiveSignals = exc;
351 emit exclusiveSignalsChanged();
355
356
357
358
359
360
361
362
363void QQuickTapHandler::setPressed(
bool press,
bool cancel, QPointerEvent *event, QEventPoint &point)
365 if (m_pressed != press) {
366 qCDebug(lcTapHandler) << objectName() <<
"pressed" << m_pressed <<
"->" << press
367 << (cancel ?
"CANCEL" :
"") << point <<
"gp" << m_gesturePolicy;
369 connectPreRenderSignal(press);
372 if (m_longPressThreshold > 0)
373 m_longPressTimer.start(m_longPressThreshold,
this);
376 m_longPressTimer.stop();
377 m_holdTimer.invalidate();
381 if (m_gesturePolicy == DragThreshold)
382 setPassiveGrab(event, point, press);
384 setExclusiveGrab(event, point, press);
386 if (!cancel && !press && parentContains(point)) {
388 qCDebug(lcTapHandler) << objectName() <<
"long press threshold" << longPressThreshold() <<
"exceeded:" << point.timeHeld();
391 const quint64 ts = event->timestamp();
392 const quint64 interval = ts - m_lastTapTimestamp;
393 const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
394 const auto singleTapReleasedButton = event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
395 if ((interval < m_multiTapInterval && distanceSquared <
396 (event->device()->type() == QInputDevice::DeviceType::Mouse ?
397 m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
398 && m_singleTapReleasedButton == singleTapReleasedButton) {
401 m_singleTapReleasedButton = singleTapReleasedButton;
402 m_singleTapReleasedPoint = point;
405 qCDebug(lcTapHandler) << objectName() <<
"tapped" << m_tapCount <<
"times; interval since last:" << interval
406 <<
"sec; distance since last:" << qSqrt(distanceSquared);
407 auto button = event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
408 emit tapped(point, button);
409 emit tapCountChanged();
410 switch (m_exclusiveSignals) {
413 emit singleTapped(point, button);
414 else if (m_tapCount == 2)
415 emit doubleTapped(point, button);
419 emit singleTapped(point, button);
423 emit doubleTapped(point, button);
425 case (SingleTap | DoubleTap):
426 if (m_tapCount == 1) {
427 qCDebug(lcTapHandler) << objectName() <<
"waiting to emit singleTapped:" << m_multiTapInterval <<
"ms";
428 m_doubleTapTimer.start(m_multiTapInterval,
this);
431 qCDebug(lcTapHandler) << objectName() <<
"tap" << m_tapCount <<
"after" << event->timestamp() - m_lastTapTimestamp <<
"ms";
433 m_lastTapTimestamp = ts;
434 m_lastTapPos = point.scenePosition();
437 m_longPressed =
false;
438 emit pressedChanged();
439 if (!press && m_gesturePolicy != DragThreshold) {
441 setExclusiveGrab(event, point, press);
444 emit canceled(point);
446 setExclusiveGrab(event, point,
false);
455void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition,
456 QPointerEvent *ev, QEventPoint &point)
459 QQuickSinglePointHandler::onGrabChanged(grabber, transition, ev, point);
462 const bool isCanceled = transition == QPointingDevice::CancelGrabExclusive || transition == QPointingDevice::CancelGrabPassive;
465 const bool passiveGrab = transition == QPointingDevice::GrabPassive || transition == QPointingDevice::UngrabPassive;
466 if (grabber ==
this && (isCanceled || point.state() == QEventPoint::Released || (!active() && !passiveGrab)))
467 setPressed(
false, isCanceled, ev, point);
470void QQuickTapHandler::connectPreRenderSignal(
bool conn)
473 disconnect(m_preRenderSignalConnection);
475 auto par = parentItem();
476 if (!par || !par->window())
480
481
482
483
484
485
486
488 m_preRenderSignalConnection = connect(par->window(), &QQuickWindow::beforeSynchronizing,
489 this, &QQuickTapHandler::updateTimeHeld);
493void QQuickTapHandler::updateTimeHeld()
495 emit timeHeldChanged();
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
539
540
541
542
543
544
545
546
547
548
549
550
551
552
555
556
557
558
559
560
561
562
563
564
565
568
569
570
571
572
573
574
575
576
577
578
579
580
583
584
585
586
587
588
589
590
593
594
595
596
597
598
601#include "moc_qquicktaphandler_p.cpp"