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);
151 if (m_gesturePolicy == DragThreshold) {
152 bool shouldReset = isTouch;
157 bool hasFilteringAncestor =
false;
158 for (QQuickItem *anc = parentItem(); anc; anc = anc->parentItem()) {
159 if (anc->filtersChildMouseEvents()) {
160 hasFilteringAncestor =
true;
164 shouldReset = !hasFilteringAncestor;
167 point.setAccepted(
false);
172
173
174
175
176
177
178
179
180
181
182
183
184
185qreal QQuickTapHandler::longPressThreshold()
const
187 return m_longPressThreshold / qreal(1000);
190void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
192 if (longPressThreshold < 0) {
193 resetLongPressThreshold();
196 int ms = qRound(longPressThreshold * 1000);
197 if (m_longPressThreshold == ms)
200 m_longPressThreshold = ms;
201 emit longPressThresholdChanged();
204void QQuickTapHandler::resetLongPressThreshold()
206 int ms = QGuiApplication::styleHints()->mousePressAndHoldInterval();
207 if (m_longPressThreshold == ms)
210 m_longPressThreshold = ms;
211 emit longPressThresholdChanged();
214void QQuickTapHandler::timerEvent(QTimerEvent *event)
216 if (event->timerId() == m_longPressTimer.timerId()) {
217 m_longPressTimer.stop();
218 qCDebug(lcTapHandler) << objectName() <<
"longPressed";
219 m_longPressed =
true;
221 }
else if (event->timerId() == m_doubleTapTimer.timerId()) {
222 m_doubleTapTimer.stop();
223 qCDebug(lcTapHandler) << objectName() <<
"double-tap timer expired; taps:" << m_tapCount;
224 Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap));
226 emit singleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
227 else if (m_tapCount == 2)
228 emit doubleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
337 if (m_gesturePolicy == gesturePolicy)
340 m_gesturePolicy = gesturePolicy;
341 emit gesturePolicyChanged();
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc)
369 if (m_exclusiveSignals == exc)
372 m_exclusiveSignals = exc;
373 emit exclusiveSignalsChanged();
377
378
379
380
381
382
383
384
385void QQuickTapHandler::setPressed(
bool press,
bool cancel, QPointerEvent *event, QEventPoint &point)
387 if (m_pressed != press) {
388 qCDebug(lcTapHandler) << objectName() <<
"pressed" << m_pressed <<
"->" << press
389 << (cancel ?
"CANCEL" :
"") << point <<
"gp" << m_gesturePolicy;
391 connectPreRenderSignal(press);
394 if (m_longPressThreshold > 0)
395 m_longPressTimer.start(m_longPressThreshold,
this);
398 m_longPressTimer.stop();
399 m_holdTimer.invalidate();
403 if (m_gesturePolicy == DragThreshold)
404 setPassiveGrab(event, point, press);
406 setExclusiveGrab(event, point, press);
408 if (!cancel && !press && parentContains(point)) {
410 qCDebug(lcTapHandler) << objectName() <<
"long press threshold" << longPressThreshold() <<
"exceeded:" << point.timeHeld();
413 const quint64 ts = event->timestamp();
414 const quint64 interval = ts - m_lastTapTimestamp;
415 const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
416 const auto singleTapReleasedButton = event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
417 if ((interval < m_multiTapInterval && distanceSquared <
418 (event->device()->type() == QInputDevice::DeviceType::Mouse ?
419 m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
420 && m_singleTapReleasedButton == singleTapReleasedButton) {
423 m_singleTapReleasedButton = singleTapReleasedButton;
424 m_singleTapReleasedPoint = point;
427 qCDebug(lcTapHandler) << objectName() <<
"tapped" << m_tapCount <<
"times; interval since last:" << interval
428 <<
"sec; distance since last:" << qSqrt(distanceSquared);
429 auto button = event->isSinglePointEvent() ?
static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
430 emit tapped(point, button);
431 emit tapCountChanged();
432 switch (m_exclusiveSignals) {
435 emit singleTapped(point, button);
436 else if (m_tapCount == 2)
437 emit doubleTapped(point, button);
441 emit singleTapped(point, button);
445 emit doubleTapped(point, button);
447 case (SingleTap | DoubleTap):
448 if (m_tapCount == 1) {
449 qCDebug(lcTapHandler) << objectName() <<
"waiting to emit singleTapped:" << m_multiTapInterval <<
"ms";
450 m_doubleTapTimer.start(m_multiTapInterval,
this);
453 qCDebug(lcTapHandler) << objectName() <<
"tap" << m_tapCount <<
"after" << event->timestamp() - m_lastTapTimestamp <<
"ms";
455 m_lastTapTimestamp = ts;
456 m_lastTapPos = point.scenePosition();
459 m_longPressed =
false;
460 emit pressedChanged();
461 if (!press && m_gesturePolicy != DragThreshold) {
463 setExclusiveGrab(event, point, press);
466 emit canceled(point);
468 setExclusiveGrab(event, point,
false);
477void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition,
478 QPointerEvent *ev, QEventPoint &point)
481 QQuickSinglePointHandler::onGrabChanged(grabber, transition, ev, point);
484 const bool isCanceled = transition == QPointingDevice::CancelGrabExclusive || transition == QPointingDevice::CancelGrabPassive;
487 const bool passiveGrab = transition == QPointingDevice::GrabPassive || transition == QPointingDevice::UngrabPassive;
488 if (grabber ==
this && (isCanceled || point.state() == QEventPoint::Released || (!active() && !passiveGrab)))
489 setPressed(
false, isCanceled, ev, point);
492void QQuickTapHandler::connectPreRenderSignal(
bool conn)
495 disconnect(m_preRenderSignalConnection);
497 auto par = parentItem();
498 if (!par || !par->window())
502
503
504
505
506
507
508
510 m_preRenderSignalConnection = connect(par->window(), &QQuickWindow::beforeSynchronizing,
511 this, &QQuickTapHandler::updateTimeHeld);
515void QQuickTapHandler::updateTimeHeld()
517 emit timeHeldChanged();
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
541
542
543
544
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
572
573
574
577
578
579
580
581
582
583
584
585
586
587
590
591
592
593
594
595
596
597
598
599
600
601
602
605
606
607
608
609
610
611
612
615
616
617
618
619
620
623#include "moc_qquicktaphandler_p.cpp"
Combined button and popup list for selecting options.