Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquicktaphandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include <QtQuick/private/qquickdeliveryagent_p_p.h>
7#include <QtQuick/qquickwindow.h>
8#include <qpa/qplatformtheme.h>
9#include <private/qguiapplication_p.h>
10#include <QtGui/qstylehints.h>
11
13
14Q_LOGGING_CATEGORY(lcTapHandler, "qt.quick.handler.tap")
15
16quint64 QQuickTapHandler::m_multiTapInterval(0);
17// single tap distance is the same as the drag threshold
18int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1);
19int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
20
58 , m_longPressThreshold(QGuiApplication::styleHints()->mousePressAndHoldInterval())
59{
60 if (m_mouseMultiClickDistanceSquared < 0) {
61 m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval();
62 m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance();
63 m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
64 m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance();
65 m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared;
66 }
67}
68
70{
74 return false;
75 // If the user has not violated any constraint, it could be a tap.
76 // Otherwise we want to give up the grab so that a competing handler
77 // (e.g. DragHandler) gets a chance to take over.
78 // Don't forget to emit released in case of a cancel.
79 bool ret = false;
80 bool overThreshold = d_func()->dragOverThreshold(point);
81 if (overThreshold && m_gesturePolicy != DragWithinBounds) {
82 if (m_longPressTimer.isActive())
83 qCDebug(lcTapHandler) << objectName() << "drag threshold exceeded";
84 m_longPressTimer.stop();
85 m_holdTimer.invalidate();
86 }
87 switch (point.state()) {
91 break;
93 ret = point.id() == this->point().id();
94 switch (m_gesturePolicy) {
95 case DragThreshold:
96 ret = ret && !overThreshold && parentContains(point);
97 break;
98 case WithinBounds:
101 break;
103 // no change to ret: depends only whether it's the already-tracking point ID
104 break;
105 }
106 break;
108 // If the point hasn't moved since last time, the return value should be the same as last time.
109 // If we return false here, QQuickPointerHandler::handlePointerEvent() will call setActive(false).
110 ret = point.id() == this->point().id();
111 break;
113 break;
114 }
115 // If this is the grabber, returning false from this function will cancel the grab,
116 // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called.
117 // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but
118 // we still don't want to be pressed anymore.
119 if (!ret && point.id() == this->point().id())
120 setPressed(false, true, const_cast<QPointerEvent *>(event), const_cast<QEventPoint &>(point));
121 return ret;
122}
123
125{
127 switch (point.state()) {
129 setPressed(true, false, event, point);
130 break;
132 if (isTouch || (static_cast<const QSinglePointEvent *>(event)->buttons() & acceptedButtons()) == Qt::NoButton)
133 setPressed(false, false, event, point);
134 break;
135 }
136 default:
137 break;
138 }
139
141
142 // If TapHandler only needs a passive grab, it should not block other items and handlers from reacting.
143 // If the point is accepted, QQuickItemPrivate::localizedTouchEvent() would skip it.
144 if (isTouch && m_gesturePolicy == DragThreshold)
145 point.setAccepted(false);
146}
147
163{
164 return m_longPressThreshold / qreal(1000);
165}
166
168{
169 if (longPressThreshold < 0) {
171 return;
172 }
173 int ms = qRound(longPressThreshold * 1000);
174 if (m_longPressThreshold == ms)
175 return;
176
177 m_longPressThreshold = ms;
179}
180
182{
183 int ms = QGuiApplication::styleHints()->mousePressAndHoldInterval();
184 if (m_longPressThreshold == ms)
185 return;
186
187 m_longPressThreshold = ms;
189}
190
192{
193 if (event->timerId() == m_longPressTimer.timerId()) {
194 m_longPressTimer.stop();
195 qCDebug(lcTapHandler) << objectName() << "longPressed";
196 m_longPressed = true;
198 } else if (event->timerId() == m_doubleTapTimer.timerId()) {
199 m_doubleTapTimer.stop();
200 qCDebug(lcTapHandler) << objectName() << "double-tap timer expired; taps:" << m_tapCount;
201 Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap));
202 if (m_tapCount == 1)
203 emit singleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
204 else if (m_tapCount == 2)
205 emit doubleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
206 }
207}
208
313{
314 if (m_gesturePolicy == gesturePolicy)
315 return;
316
317 m_gesturePolicy = gesturePolicy;
319}
320
344void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc)
345{
346 if (m_exclusiveSignals == exc)
347 return;
348
349 m_exclusiveSignals = exc;
350 emit exclusiveSignalsChanged();
351}
352
362void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point)
363{
364 if (m_pressed != press) {
365 qCDebug(lcTapHandler) << objectName() << "pressed" << m_pressed << "->" << press
366 << (cancel ? "CANCEL" : "") << point << "gp" << m_gesturePolicy;
367 m_pressed = press;
368 connectPreRenderSignal(press);
369 updateTimeHeld();
370 if (press) {
371 if (m_longPressThreshold > 0)
372 m_longPressTimer.start(m_longPressThreshold, this);
373 m_holdTimer.start();
374 } else {
375 m_longPressTimer.stop();
376 m_holdTimer.invalidate();
377 }
378 if (press) {
379 // on press, grab before emitting changed signals
380 if (m_gesturePolicy == DragThreshold)
381 setPassiveGrab(event, point, press);
382 else
384 }
385 if (!cancel && !press && parentContains(point)) {
386 if (m_longPressed) {
387 qCDebug(lcTapHandler) << objectName() << "long press threshold" << longPressThreshold() << "exceeded:" << point.timeHeld();
388 } else {
389 // Assuming here that pointerEvent()->timestamp() is in ms.
390 const quint64 ts = event->timestamp();
391 const quint64 interval = ts - m_lastTapTimestamp;
392 const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
393 const auto singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
394 if ((interval < m_multiTapInterval && distanceSquared <
395 (event->device()->type() == QInputDevice::DeviceType::Mouse ?
396 m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
397 && m_singleTapReleasedButton == singleTapReleasedButton) {
398 ++m_tapCount;
399 } else {
400 m_singleTapReleasedButton = singleTapReleasedButton;
401 m_singleTapReleasedPoint = point;
402 m_tapCount = 1;
403 }
404 qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times; interval since last:" << interval
405 << "sec; distance since last:" << qSqrt(distanceSquared);
406 auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
409 switch (m_exclusiveSignals) {
410 case NotExclusive:
411 if (m_tapCount == 1)
413 else if (m_tapCount == 2)
415 break;
416 case SingleTap:
417 if (m_tapCount == 1)
419 break;
420 case DoubleTap:
421 if (m_tapCount == 2)
423 break;
424 case (SingleTap | DoubleTap):
425 if (m_tapCount == 1) {
426 qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << m_multiTapInterval << "ms";
427 m_doubleTapTimer.start(m_multiTapInterval, this);
428 }
429 }
430 qCDebug(lcTapHandler) << objectName() << "tap" << m_tapCount << "after" << event->timestamp() - m_lastTapTimestamp << "ms";
431
432 m_lastTapTimestamp = ts;
433 m_lastTapPos = point.scenePosition();
434 }
435 }
436 m_longPressed = false;
438 if (!press && m_gesturePolicy != DragThreshold) {
439 // on release, ungrab after emitting changed signals
441 }
442 if (cancel) {
445 // In case there is a filtering parent (Flickable), we should not give up the passive grab,
446 // so that it can continue to filter future events.
447 d_func()->reset();
449 }
450 }
451}
452
454 QPointerEvent *ev, QEventPoint &point)
455{
456 QQuickSinglePointHandler::onGrabChanged(grabber, transition, ev, point);
457 bool isCanceled = transition == QPointingDevice::CancelGrabExclusive || transition == QPointingDevice::CancelGrabPassive;
458 if (grabber == this && (isCanceled || point.state() == QEventPoint::Released))
459 setPressed(false, isCanceled, ev, point);
460}
461
462void QQuickTapHandler::connectPreRenderSignal(bool conn)
463{
464 // disconnect pre-existing connection, if any
465 disconnect(m_preRenderSignalConnection);
466
467 auto par = parentItem();
468 if (!par || !par->window())
469 return;
470
471 /*
472 Note: beforeSynchronizing is emitted from the SG thread, and the
473 timeHeldChanged signal can be used to do arbitrary things in user QML.
474
475 But the docs say the GUI thread is blockd, and "Therefore, it is safe
476 to access GUI thread thread data in a slot or lambda that is connected
477 with Qt::DirectConnection." We use the default AutoConnection just in case.
478 */
479 if (conn) {
480 m_preRenderSignalConnection = connect(par->window(), &QQuickWindow::beforeSynchronizing,
481 this, &QQuickTapHandler::updateTimeHeld);
482 }
483}
484
485void QQuickTapHandler::updateTimeHeld()
486{
488}
489
592
593#include "moc_qquicktaphandler_p.cpp"
void start(int msec, QObject *obj)
\obsolete Use chrono overload instead.
int timerId() const noexcept
Returns the timer's ID.
Definition qbasictimer.h:35
void stop()
Stops the timer.
bool isActive() const noexcept
Returns true if the timer is running and has not been stopped; otherwise returns false.
Definition qbasictimer.h:34
void invalidate() noexcept
Marks this QElapsedTimer object as invalid.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
The QEventPoint class provides information about a point in a QPointerEvent.
Definition qeventpoint.h:20
\macro qGuiApp
static QStyleHints * styleHints()
Returns the application's style hints.
QString objectName
the name of this object
Definition qobject.h:107
A base class for pointer events.
Definition qevent.h:73
GrabTransition
This enum represents a transition of exclusive or passive grab from one object (possibly nullptr) to ...
static bool isTabletEvent(const QPointerEvent *ev)
static bool isTouchEvent(const QPointerEvent *ev)
static bool isMouseEvent(const QPointerEvent *ev)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
bool parentContains(const QEventPoint &point) const
Returns true if margin() > 0 and point is within the margin beyond QQuickItem::boundingRect(),...
QQuickItem * parentItem() const
\qmlproperty Item QtQuick::PointerHandler::parent
void setPassiveGrab(QPointerEvent *event, const QEventPoint &point, bool grab=true)
Acquire or give up a passive grab of the given point, according to the grab state.
bool setExclusiveGrab(QPointerEvent *ev, const QEventPoint &point, bool grab=true)
Acquire or give up the exclusive grab of the given point, according to the grab state,...
void canceled(QEventPoint point)
virtual void handleEventPoint(QPointerEvent *event, QEventPoint &point)
void onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, QPointerEvent *event, QEventPoint &point) override
Notification that the grab has changed in some way which is relevant to this handler.
void handleEventPoint(QPointerEvent *event, QEventPoint &point) override
void doubleTapped(QEventPoint eventPoint, Qt::MouseButton)
void setExclusiveSignals(QQuickTapHandler::ExclusiveSignals newexclusiveSignals)
\qmlproperty enumeration QtQuick::TapHandler::exclusiveSignals
void setGesturePolicy(GesturePolicy gesturePolicy)
\qmlproperty enumeration QtQuick::TapHandler::gesturePolicy
GesturePolicy gesturePolicy
void onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, QPointerEvent *ev, QEventPoint &point) override
Notification that the grab has changed in some way which is relevant to this handler.
void singleTapped(QEventPoint eventPoint, Qt::MouseButton)
void setLongPressThreshold(qreal longPressThreshold)
void timerEvent(QTimerEvent *event) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
void tapCountChanged()
void gesturePolicyChanged()
void tapped(QEventPoint eventPoint, Qt::MouseButton)
void longPressThresholdChanged()
bool wantsEventPoint(const QPointerEvent *event, const QEventPoint &point) override
Returns true if the given point (as part of event) could be relevant at all to this handler,...
void timeHeldChanged()
void beforeSynchronizing()
This signal is emitted before the scene graph is synchronized with the QML state.
A base class for pointer events containing a single point, such as mouse events.
Definition qevent.h:109
\inmodule QtCore
Definition qcoreevent.h:366
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float lengthSquared() const noexcept
Returns the squared length of the vector from the origin.
Definition qvectornd.h:524
#define this
Definition dialogs.cpp:9
QPushButton * button
[2]
Combined button and popup list for selecting options.
@ NoButton
Definition qnamespace.h:57
#define qApp
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
struct _cl_event * event
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
unsigned long long quint64
Definition qtypes.h:61
double qreal
Definition qtypes.h:187
future cancel()
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
myObject disconnect()
[26]