Add TapHandler.exclusiveSignals to enable single/double tap exclusivity

If exclusiveSignals == NotExclusive (the default), behavior remains as
it was: singleTapped() and doubleTapped() are emitted as the taps occur,
so it's not very useful to react on singleTapped() if you mean to
distinguish these two cases.

If exclusiveSignals == SingleTap, the doubleTapped signal will not be
emitted at all, and therefore singleTapped can be emitted immediately
and unambiguously.

If exclusiveSignals == DoubleTap, the singleTapped signal will not be
emitted at all, and therefore doubleTapped can be emitted immediately
and unambiguously.

If exclusiveSignals == SingleTap | DoubleTap, we must wait
qApp->styleHints()->mouseDoubleClickInterval() milliseconds after a tap
is detected before emitting either signal, so that they are distinct and
can be used to drive behavior that should not occur in other cases.
A triple-tap will not trigger either signal.

[ChangeLog][QtQuick][Event Handlers] TapHandler.exclusiveSignals now
lets you make the singleTapped and doubleTapped signals exclusive.

Task-number: QTBUG-65088
Fixes: QTBUG-107264
Change-Id: Ifb2c4b72759246c64b3bfa2f776c28266806b985
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io>
This commit is contained in:
Shawn Rutledge 2022-10-06 17:50:17 +02:00
parent e3343eb83f
commit d3f2c6ac42
6 changed files with 233 additions and 26 deletions

View File

@ -11,6 +11,7 @@ Rectangle {
property alias hovered: hoverHandler.hovered property alias hovered: hoverHandler.hovered
property alias gesturePolicy: tap.gesturePolicy property alias gesturePolicy: tap.gesturePolicy
property alias margin: tap.margin property alias margin: tap.margin
property alias exclusiveSignals: tap.exclusiveSignals
signal tapped signal tapped
implicitHeight: Math.max(Screen.pixelDensity * 7, label.implicitHeight * 2) implicitHeight: Math.max(Screen.pixelDensity * 7, label.implicitHeight * 2)
@ -29,7 +30,7 @@ Rectangle {
id: tap id: tap
margin: 10 // the user can tap a little beyond the edges margin: 10 // the user can tap a little beyond the edges
objectName: label.text + " Tap" objectName: label.text + " Tap"
onTapped: { onSingleTapped: {
tapFlash.start() tapFlash.start()
root.tapped() root.tapped()
} }

View File

@ -30,6 +30,7 @@ Item {
text: "Launch Missile" text: "Launch Missile"
Layout.fillWidth: true Layout.fillWidth: true
gesturePolicy: TapHandler.ReleaseWithinBounds gesturePolicy: TapHandler.ReleaseWithinBounds
exclusiveSignals: TapHandler.SingleTap
onTapped: missileEmitter.burst(1) onTapped: missileEmitter.burst(1)
Text { Text {
anchors { top: parent.bottom; horizontalCenter: parent.horizontalCenter } anchors { top: parent.bottom; horizontalCenter: parent.horizontalCenter }

View File

@ -179,6 +179,14 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
m_longPressTimer.stop(); m_longPressTimer.stop();
qCDebug(lcTapHandler) << objectName() << "longPressed"; qCDebug(lcTapHandler) << objectName() << "longPressed";
emit longPressed(); emit longPressed();
} else if (event->timerId() == m_doubleTapTimer.timerId()) {
m_doubleTapTimer.stop();
qCDebug(lcTapHandler) << objectName() << "double-tap timer expired; taps:" << m_tapCount;
Q_ASSERT(m_exclusiveSignals == (SingleTap | DoubleTap));
if (m_tapCount == 1)
emit singleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
else if (m_tapCount == 2)
emit doubleTapped(m_singleTapReleasedPoint, m_singleTapReleasedButton);
} }
} }
@ -247,6 +255,38 @@ void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gestureP
emit gesturePolicyChanged(); emit gesturePolicyChanged();
} }
/*!
\qmlproperty enumeration QtQuick::TapHandler::exclusiveSignals
\since 6.5
Determines the exclusivity of the singleTapped() and doubleTapped() signals.
\value NotExclusive (the default) singleTapped() and doubleTapped() are
emitted immediately when the user taps once or twice, respectively.
\value SingleTap singleTapped() is emitted immediately when the user taps
once, and doubleTapped() is never emitted.
\value DoubleTap doubleTapped() is emitted immediately when the user taps
twice, and singleTapped() is never emitted.
\value (SingleTap | DoubleTap) Both signals are delayed until
QStyleHints::mouseDoubleClickInterval(), such that either singleTapped()
or doubleTapped() can be emitted, but not both. But if 3 or more taps
occur within \c mouseDoubleClickInterval, neither signal is emitted.
\note The remaining signals such as tapped() and tapCountChanged() are
always emitted immediately, regardless of this property.
*/
void QQuickTapHandler::setExclusiveSignals(QQuickTapHandler::ExclusiveSignals exc)
{
if (m_exclusiveSignals == exc)
return;
m_exclusiveSignals = exc;
emit exclusiveSignalsChanged();
}
/*! /*!
\qmlproperty bool QtQuick::TapHandler::pressed \qmlproperty bool QtQuick::TapHandler::pressed
\readonly \readonly
@ -270,6 +310,16 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
} else { } else {
m_longPressTimer.stop(); m_longPressTimer.stop();
m_holdTimer.invalidate(); m_holdTimer.invalidate();
if (m_exclusiveSignals == (SingleTap | DoubleTap)) {
if (m_tapCount == 0) {
m_singleTapReleasedPoint = point;
m_singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << qApp->styleHints()->mouseDoubleClickInterval() << "ms";
m_doubleTapTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), this);
} else if (m_doubleTapTimer.isActive()) {
qCDebug(lcTapHandler) << objectName() << "tap" << (m_tapCount + 1) << "after" << event->timestamp() / 1000.0 - m_lastTapTimestamp << "sec";
}
}
} }
if (press) { if (press) {
// on press, grab before emitting changed signals // on press, grab before emitting changed signals
@ -281,22 +331,25 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
if (!cancel && !press && parentContains(point)) { if (!cancel && !press && parentContains(point)) {
if (point.timeHeld() < longPressThreshold()) { if (point.timeHeld() < longPressThreshold()) {
// Assuming here that pointerEvent()->timestamp() is in ms. // Assuming here that pointerEvent()->timestamp() is in ms.
qreal ts = event->timestamp() / 1000.0; const qreal ts = event->timestamp() / 1000.0;
if (ts - m_lastTapTimestamp < m_multiTapInterval && const qreal interval = ts - m_lastTapTimestamp;
QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared() < const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
if (interval < m_multiTapInterval && distanceSquared <
(event->device()->type() == QInputDevice::DeviceType::Mouse ? (event->device()->type() == QInputDevice::DeviceType::Mouse ?
m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared)) m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
++m_tapCount; ++m_tapCount;
else else
m_tapCount = 1; m_tapCount = 1;
qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times"; qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times; interval since last:" << interval
<< "sec; distance since last:" << qSqrt(distanceSquared);
auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton; auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
emit tapped(point, button); emit tapped(point, button);
emit tapCountChanged(); emit tapCountChanged();
if (m_tapCount == 1) if (m_tapCount == 1 && !m_exclusiveSignals.testFlag(DoubleTap))
emit singleTapped(point, button); emit singleTapped(point, button);
else if (m_tapCount == 2) else if (m_tapCount == 2 && !m_exclusiveSignals.testFlag(SingleTap)) {
emit doubleTapped(point, button); emit doubleTapped(point, button);
}
m_lastTapTimestamp = ts; m_lastTapTimestamp = ts;
m_lastTapPos = point.scenePosition(); m_lastTapPos = point.scenePosition();
} else { } else {

View File

@ -30,6 +30,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
Q_PROPERTY(qreal timeHeld READ timeHeld NOTIFY timeHeldChanged) Q_PROPERTY(qreal timeHeld READ timeHeld NOTIFY timeHeldChanged)
Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged) Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged)
Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged) Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged)
Q_PROPERTY(QQuickTapHandler::ExclusiveSignals exclusiveSignals READ exclusiveSignals WRITE setExclusiveSignals NOTIFY exclusiveSignalsChanged REVISION(6, 5))
QML_NAMED_ELEMENT(TapHandler) QML_NAMED_ELEMENT(TapHandler)
QML_ADDED_IN_VERSION(2, 12) QML_ADDED_IN_VERSION(2, 12)
@ -43,6 +44,14 @@ public:
}; };
Q_ENUM(GesturePolicy) Q_ENUM(GesturePolicy)
enum ExclusiveSignal {
NotExclusive = 0,
SingleTap = 1 << 1,
DoubleTap = 1 << 2
};
Q_DECLARE_FLAGS(ExclusiveSignals, ExclusiveSignal)
Q_FLAG(ExclusiveSignal)
explicit QQuickTapHandler(QQuickItem *parent = nullptr); explicit QQuickTapHandler(QQuickItem *parent = nullptr);
bool isPressed() const { return m_pressed; } bool isPressed() const { return m_pressed; }
@ -56,12 +65,16 @@ public:
GesturePolicy gesturePolicy() const { return m_gesturePolicy; } GesturePolicy gesturePolicy() const { return m_gesturePolicy; }
void setGesturePolicy(GesturePolicy gesturePolicy); void setGesturePolicy(GesturePolicy gesturePolicy);
QQuickTapHandler::ExclusiveSignals exclusiveSignals() const { return m_exclusiveSignals; }
void setExclusiveSignals(QQuickTapHandler::ExclusiveSignals newexclusiveSignals);
Q_SIGNALS: Q_SIGNALS:
void pressedChanged(); void pressedChanged();
void tapCountChanged(); void tapCountChanged();
void timeHeldChanged(); void timeHeldChanged();
void longPressThresholdChanged(); void longPressThresholdChanged();
void gesturePolicyChanged(); void gesturePolicyChanged();
Q_REVISION(6, 5) void exclusiveSignalsChanged();
// the second argument (Qt::MouseButton) was added in 6.2: avoid name clashes with IDs by not naming it for now // the second argument (Qt::MouseButton) was added in 6.2: avoid name clashes with IDs by not naming it for now
void tapped(QEventPoint eventPoint, Qt::MouseButton /* button */); void tapped(QEventPoint eventPoint, Qt::MouseButton /* button */);
void singleTapped(QEventPoint eventPoint, Qt::MouseButton /* button */); void singleTapped(QEventPoint eventPoint, Qt::MouseButton /* button */);
@ -86,9 +99,13 @@ private:
qreal m_lastTapTimestamp = 0; qreal m_lastTapTimestamp = 0;
QElapsedTimer m_holdTimer; QElapsedTimer m_holdTimer;
QBasicTimer m_longPressTimer; QBasicTimer m_longPressTimer;
QBasicTimer m_doubleTapTimer;
QEventPoint m_singleTapReleasedPoint;
Qt::MouseButton m_singleTapReleasedButton;
int m_tapCount = 0; int m_tapCount = 0;
int m_longPressThreshold = -1; int m_longPressThreshold = -1;
GesturePolicy m_gesturePolicy = GesturePolicy::DragThreshold; GesturePolicy m_gesturePolicy = GesturePolicy::DragThreshold;
ExclusiveSignals m_exclusiveSignals = NotExclusive;
bool m_pressed = false; bool m_pressed = false;
static qreal m_multiTapInterval; static qreal m_multiTapInterval;

View File

@ -26,7 +26,10 @@ Rectangle {
id: tap id: tap
objectName: label.text objectName: label.text
longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped
onSingleTapped: console.log("Single tap")
onDoubleTapped: console.log("Double tap")
onTapped: { onTapped: {
console.log("Tapped")
tapFlash.start() tapFlash.start()
root.tappedPosition = point.scenePosition root.tappedPosition = point.scenePosition
root.tapped() root.tapped()

View File

@ -42,7 +42,10 @@ private slots:
void gesturePolicyDragWithinBounds_data(); void gesturePolicyDragWithinBounds_data();
void gesturePolicyDragWithinBounds(); void gesturePolicyDragWithinBounds();
void touchMultiTap(); void touchMultiTap();
void mouseMultiTap_data();
void mouseMultiTap(); void mouseMultiTap();
void singleTapDoubleTap_data();
void singleTapDoubleTap();
void touchLongPress(); void touchLongPress();
void mouseLongPress(); void mouseLongPress();
void buttonsMultiTouch(); void buttonsMultiTouch();
@ -523,8 +526,32 @@ void tst_TapHandler::touchMultiTap()
QCOMPARE(tappedSpy.size(), 4); QCOMPARE(tappedSpy.size(), 4);
} }
void tst_TapHandler::mouseMultiTap_data()
{
QTest::addColumn<QQuickTapHandler::ExclusiveSignals>("exclusiveSignals");
QTest::addColumn<int>("expectedSingleTaps");
QTest::addColumn<int>("expectedSingleTapsAfterMovingAway");
QTest::addColumn<int>("expectedSingleTapsAfterWaiting");
QTest::addColumn<int>("expectedDoubleTaps");
QTest::newRow("NotExclusive") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive)
<< 1 << 2 << 3 << 1;
QTest::newRow("SingleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap)
<< 1 << 2 << 3 << 0;
QTest::newRow("DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap)
<< 0 << 0 << 0 << 1;
QTest::newRow("SingleTap|DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap)
<< 0 << 0 << 0 << 0;
}
void tst_TapHandler::mouseMultiTap() void tst_TapHandler::mouseMultiTap()
{ {
QFETCH(QQuickTapHandler::ExclusiveSignals, exclusiveSignals);
QFETCH(int, expectedSingleTaps);
QFETCH(int, expectedSingleTapsAfterMovingAway);
QFETCH(int, expectedSingleTapsAfterWaiting);
QFETCH(int, expectedDoubleTaps);
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
QScopedPointer<QQuickView> windowPtr; QScopedPointer<QQuickView> windowPtr;
createView(windowPtr, "buttons.qml"); createView(windowPtr, "buttons.qml");
@ -532,38 +559,143 @@ void tst_TapHandler::mouseMultiTap()
QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
QVERIFY(button); QVERIFY(button);
QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>();
QVERIFY(tapHandler);
tapHandler->setExclusiveSignals(exclusiveSignals);
QSignalSpy tappedSpy(button, SIGNAL(tapped())); QSignalSpy tappedSpy(button, SIGNAL(tapped()));
QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped);
QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped);
// Tap once // Click once
QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint();
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
QTRY_VERIFY(button->property("pressed").toBool()); QTRY_VERIFY(button->property("pressed").toBool());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
QTRY_VERIFY(!button->property("pressed").toBool()); QTRY_VERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 1); QCOMPARE(tappedSpy.size(), 1);
// If exclusiveSignals == SingleTap | DoubleTap:
// This would be a single-click if we waited longer than the double-click interval,
// but it's too early for the signal at this moment; and we're going to click again.
// If exclusiveSignals == DoubleTap: singleTapped() won't happen.
// Otherwise: we got singleTapped() immediately.
QCOMPARE(singleTapSpy.size(), expectedSingleTaps);
QCOMPARE(tapHandler->timeHeld(), -1);
// Tap again in exactly the same place (not likely with touch in the real world) // Click again in exactly the same place
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
QTRY_VERIFY(button->property("pressed").toBool());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
QTRY_VERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 2); QCOMPARE(tappedSpy.size(), 2);
QCOMPARE(singleTapSpy.size(), expectedSingleTaps);
QCOMPARE(doubleTapSpy.size(), expectedDoubleTaps);
// Tap a third time, nearby // Click a third time, nearby: that'll be a triple-click
p1 += QPoint(dragThreshold, dragThreshold); p1 += QPoint(1, 1);
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
QTRY_VERIFY(button->property("pressed").toBool());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
QTRY_VERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 3); QCOMPARE(tappedSpy.size(), 3);
QCOMPARE(singleTapSpy.size(), expectedSingleTaps);
QCOMPARE(doubleTapSpy.size(), expectedDoubleTaps);
QCOMPARE(tapHandler->tapCount(), 3);
// Tap a fourth time, drifting farther away // Click a fourth time, drifting farther away: treated as a separate click, regardless of timing
p1 += QPoint(dragThreshold, dragThreshold); p1 += QPoint(dragThreshold, dragThreshold);
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1); // default delay to prevent double-click
QTRY_VERIFY(button->property("pressed").toBool());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
QTRY_VERIFY(!button->property("pressed").toBool());
QCOMPARE(tappedSpy.size(), 4); QCOMPARE(tappedSpy.size(), 4);
QCOMPARE(tapHandler->tapCount(), 1);
QTRY_COMPARE(singleTapSpy.size(), expectedSingleTapsAfterMovingAway);
// Click a fifth time later on at the same place: treated as a separate click
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1);
QCOMPARE(tappedSpy.size(), 5);
QCOMPARE(tapHandler->tapCount(), 1);
QCOMPARE(singleTapSpy.size(), expectedSingleTapsAfterWaiting);
}
void tst_TapHandler::singleTapDoubleTap_data()
{
QTest::addColumn<QPointingDevice::DeviceType>("deviceType");
QTest::addColumn<QQuickTapHandler::ExclusiveSignals>("exclusiveSignals");
QTest::addColumn<int>("expectedEndingSingleTapCount");
QTest::addColumn<int>("expectedDoubleTapCount");
QTest::newRow("mouse:NotExclusive")
<< QPointingDevice::DeviceType::Mouse
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive)
<< 1 << 1;
QTest::newRow("mouse:SingleTap")
<< QPointingDevice::DeviceType::Mouse
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap)
<< 1 << 0;
QTest::newRow("mouse:DoubleTap")
<< QPointingDevice::DeviceType::Mouse
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap)
<< 0 << 1;
QTest::newRow("mouse:SingleTap|DoubleTap")
<< QPointingDevice::DeviceType::Mouse
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap)
<< 0 << 1;
QTest::newRow("touch:NotExclusive")
<< QPointingDevice::DeviceType::TouchScreen
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive)
<< 1 << 1;
QTest::newRow("touch:SingleTap")
<< QPointingDevice::DeviceType::TouchScreen
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap)
<< 1 << 0;
QTest::newRow("touch:DoubleTap")
<< QPointingDevice::DeviceType::TouchScreen
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap)
<< 0 << 1;
QTest::newRow("touch:SingleTap|DoubleTap")
<< QPointingDevice::DeviceType::TouchScreen
<< QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap)
<< 0 << 1;
}
void tst_TapHandler::singleTapDoubleTap()
{
QFETCH(QPointingDevice::DeviceType, deviceType);
QFETCH(QQuickTapHandler::ExclusiveSignals, exclusiveSignals);
QFETCH(int, expectedEndingSingleTapCount);
QFETCH(int, expectedDoubleTapCount);
QScopedPointer<QQuickView> windowPtr;
createView(windowPtr, "buttons.qml");
QQuickView * window = windowPtr.data();
QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
QVERIFY(button);
QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>();
QVERIFY(tapHandler);
tapHandler->setExclusiveSignals(exclusiveSignals);
QSignalSpy tappedSpy(tapHandler, &QQuickTapHandler::tapped);
QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped);
QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped);
auto tap = [window, tapHandler, deviceType, this](const QPoint &p1) {
switch (static_cast<QPointingDevice::DeviceType>(deviceType)) {
case QPointingDevice::DeviceType::Mouse:
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
break;
case QPointingDevice::DeviceType::TouchScreen:
QTest::touchEvent(window, touchDevice).press(0, p1, window);
QTRY_VERIFY(tapHandler->isPressed());
QTest::touchEvent(window, touchDevice).release(0, p1, window);
break;
default:
break;
}
};
// tap once
const QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint();
tap(p1);
QCOMPARE(tappedSpy.size(), 1);
QCOMPARE(doubleTapSpy.size(), 0);
// tap again immediately afterwards
tap(p1);
QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount);
QCOMPARE(tappedSpy.size(), 2);
QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount);
} }
void tst_TapHandler::touchLongPress() void tst_TapHandler::touchLongPress()