Qt 6.x
The Qt SDK
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{
59 if (m_mouseMultiClickDistanceSquared < 0) {
60 m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval();
61 m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance();
62 m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
63 m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance();
64 m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared;
65 }
66}
67
69{
73 return false;
74 // If the user has not violated any constraint, it could be a tap.
75 // Otherwise we want to give up the grab so that a competing handler
76 // (e.g. DragHandler) gets a chance to take over.
77 // Don't forget to emit released in case of a cancel.
78 bool ret = false;
79 bool overThreshold = d_func()->dragOverThreshold(point);
80 if (overThreshold && m_gesturePolicy != DragWithinBounds) {
81 m_longPressTimer.stop();
82 m_holdTimer.invalidate();
83 }
84 switch (point.state()) {
88 break;
90 ret = point.id() == this->point().id();
91 switch (m_gesturePolicy) {
92 case DragThreshold:
93 ret = ret && !overThreshold && parentContains(point);
94 break;
95 case WithinBounds:
98 break;
100 // no change to ret: depends only whether it's the already-tracking point ID
101 break;
102 }
103 break;
105 // If the point hasn't moved since last time, the return value should be the same as last time.
106 // If we return false here, QQuickPointerHandler::handlePointerEvent() will call setActive(false).
107 ret = point.id() == this->point().id();
108 break;
110 break;
111 }
112 // If this is the grabber, returning false from this function will cancel the grab,
113 // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called.
114 // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but
115 // we still don't want to be pressed anymore.
116 if (!ret && point.id() == this->point().id())
117 setPressed(false, true, const_cast<QPointerEvent *>(event), const_cast<QEventPoint &>(point));
118 return ret;
119}
120
122{
124 switch (point.state()) {
126 setPressed(true, false, event, point);
127 break;
129 if (isTouch || (static_cast<const QSinglePointEvent *>(event)->buttons() & acceptedButtons()) == Qt::NoButton)
130 setPressed(false, false, event, point);
131 break;
132 }
133 default:
134 break;
135 }
136
138
139 // If TapHandler only needs a passive grab, it should not block other items and handlers from reacting.
140 // If the point is accepted, QQuickItemPrivate::localizedTouchEvent() would skip it.
141 if (isTouch && m_gesturePolicy == DragThreshold)
142 point.setAccepted(false);
143}
144
155{
156 return longPressThresholdMilliseconds() / 1000.0;
157}
158
160{
161 int ms = qRound(longPressThreshold * 1000);
162 if (m_longPressThreshold == ms)
163 return;
164
165 m_longPressThreshold = ms;
167}
168
169int QQuickTapHandler::longPressThresholdMilliseconds() const
170{
171 return (m_longPressThreshold < 0 ? QGuiApplication::styleHints()->mousePressAndHoldInterval() : m_longPressThreshold);
172}
173
175{
176 if (event->timerId() == m_longPressTimer.timerId()) {
177 m_longPressTimer.stop();
178 qCDebug(lcTapHandler) << objectName() << "longPressed";
180 } else if (event->timerId() == m_doubleTapTimer.timerId()) {
181 m_doubleTapTimer.stop();
182 qCDebug(lcTapHandler) << objectName() << "double-tap timer expired; taps:" << m_tapCount;
183 Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap));
184 if (m_tapCount == 1)
185 emit singleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
186 else if (m_tapCount == 2)
187 emit doubleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
188 }
189}
190
295{
296 if (m_gesturePolicy == gesturePolicy)
297 return;
298
299 m_gesturePolicy = gesturePolicy;
301}
302
326void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc)
327{
328 if (m_exclusiveSignals == exc)
329 return;
330
331 m_exclusiveSignals = exc;
332 emit exclusiveSignalsChanged();
333}
334
344void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point)
345{
346 if (m_pressed != press) {
347 qCDebug(lcTapHandler) << objectName() << "pressed" << m_pressed << "->" << press
348 << (cancel ? "CANCEL" : "") << point << "gp" << m_gesturePolicy;
349 m_pressed = press;
350 connectPreRenderSignal(press);
351 updateTimeHeld();
352 if (press) {
353 m_longPressTimer.start(longPressThresholdMilliseconds(), this);
354 m_holdTimer.start();
355 } else {
356 m_longPressTimer.stop();
357 m_holdTimer.invalidate();
358 }
359 if (press) {
360 // on press, grab before emitting changed signals
361 if (m_gesturePolicy == DragThreshold)
362 setPassiveGrab(event, point, press);
363 else
365 }
366 if (!cancel && !press && parentContains(point)) {
367 if (point.timeHeld() < longPressThreshold()) {
368 // Assuming here that pointerEvent()->timestamp() is in ms.
369 const quint64 ts = event->timestamp();
370 const quint64 interval = ts - m_lastTapTimestamp;
371 const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
372 const auto singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
373 if ((interval < m_multiTapInterval && distanceSquared <
374 (event->device()->type() == QInputDevice::DeviceType::Mouse ?
375 m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
376 && m_singleTapReleasedButton == singleTapReleasedButton) {
377 ++m_tapCount;
378 } else {
379 m_singleTapReleasedButton = singleTapReleasedButton;
380 m_singleTapReleasedPoint = point;
381 m_tapCount = 1;
382 }
383 qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times; interval since last:" << interval
384 << "sec; distance since last:" << qSqrt(distanceSquared);
385 auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
388 switch (m_exclusiveSignals) {
389 case NotExclusive:
390 if (m_tapCount == 1)
392 else if (m_tapCount == 2)
394 break;
395 case SingleTap:
396 if (m_tapCount == 1)
398 break;
399 case DoubleTap:
400 if (m_tapCount == 2)
402 break;
403 case (SingleTap | DoubleTap):
404 if (m_tapCount == 1) {
405 qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << m_multiTapInterval << "ms";
406 m_doubleTapTimer.start(m_multiTapInterval, this);
407 }
408 }
409 qCDebug(lcTapHandler) << objectName() << "tap" << m_tapCount << "after" << event->timestamp() - m_lastTapTimestamp << "ms";
410
411 m_lastTapTimestamp = ts;
412 m_lastTapPos = point.scenePosition();
413 } else {
414 qCDebug(lcTapHandler) << objectName() << "tap threshold" << longPressThreshold() << "exceeded:" << point.timeHeld();
415 }
416 }
418 if (!press && m_gesturePolicy != DragThreshold) {
419 // on release, ungrab after emitting changed signals
421 }
422 if (cancel) {
425 // In case there is a filtering parent (Flickable), we should not give up the passive grab,
426 // so that it can continue to filter future events.
427 d_func()->reset();
429 }
430 }
431}
432
434 QPointerEvent *ev, QEventPoint &point)
435{
436 QQuickSinglePointHandler::onGrabChanged(grabber, transition, ev, point);
437 bool isCanceled = transition == QPointingDevice::CancelGrabExclusive || transition == QPointingDevice::CancelGrabPassive;
438 if (grabber == this && (isCanceled || point.state() == QEventPoint::Released))
439 setPressed(false, isCanceled, ev, point);
440}
441
442void QQuickTapHandler::connectPreRenderSignal(bool conn)
443{
444 auto par = parentItem();
445 if (!par)
446 return;
447 if (conn)
448 connect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
449 else
450 disconnect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
451}
452
453void QQuickTapHandler::updateTimeHeld()
454{
456}
457
560
561#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.
void invalidate() noexcept
Marks this QElapsedTimer object as invalid.
void start() noexcept
Starts this timer.
The QEventPoint class provides information about a point in a QPointerEvent.
Definition qeventpoint.h:20
static QStyleHints * styleHints()
Returns the application's style hints.
QString objectName
the name of this object
Definition qobject.h:94
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:64
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:108
\inmodule QtCore
Definition qcoreevent.h:359
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:56
#define qApp
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:243
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#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:56
double qreal
Definition qtypes.h:92
future cancel()
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
myObject disconnect()
[26]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent