Fix TapHandler signals in combination with exclusiveSignals

TapHandler does not emit any singleTapped or doubleTapped signals after
emitting it once, if exclusiveSignals is set to `SingleTap | DoubleTap`.

This change corrects the behavior by resetting m_tapCount properly.

Fixes: QTBUG-111800
Pick-to: 6.5
Change-Id: Ice7af2f41c2f30448004033d8330e733abe44110
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
Shawn Rutledge 2023-03-07 14:12:18 +01:00
parent 07aaa7c1b6
commit 951ab9f3c0
3 changed files with 49 additions and 22 deletions

View File

@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTapHandler, "qt.quick.handler.tap")
qreal QQuickTapHandler::m_multiTapInterval(0.0);
quint64 QQuickTapHandler::m_multiTapInterval(0);
// single tap distance is the same as the drag threshold
int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1);
int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
@ -57,7 +57,7 @@ QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
: QQuickSinglePointHandler(parent)
{
if (m_mouseMultiClickDistanceSquared < 0) {
m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0;
m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval();
m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance();
m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance();
@ -355,16 +355,6 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
} else {
m_longPressTimer.stop();
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) {
// on press, grab before emitting changed signals
@ -376,8 +366,8 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
if (!cancel && !press && parentContains(point)) {
if (point.timeHeld() < longPressThreshold()) {
// Assuming here that pointerEvent()->timestamp() is in ms.
const qreal ts = event->timestamp() / 1000.0;
const qreal interval = ts - m_lastTapTimestamp;
const quint64 ts = event->timestamp();
const quint64 interval = ts - m_lastTapTimestamp;
const auto distanceSquared = QVector2D(point.scenePosition() - m_lastTapPos).lengthSquared();
const auto singleTapReleasedButton = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
if ((interval < m_multiTapInterval && distanceSquared <
@ -387,6 +377,7 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
++m_tapCount;
} else {
m_singleTapReleasedButton = singleTapReleasedButton;
m_singleTapReleasedPoint = point;
m_tapCount = 1;
}
qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times; interval since last:" << interval
@ -394,11 +385,29 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
emit tapped(point, button);
emit tapCountChanged();
if (m_tapCount == 1 && !m_exclusiveSignals.testFlag(DoubleTap))
emit singleTapped(point, button);
else if (m_tapCount == 2 && !m_exclusiveSignals.testFlag(SingleTap)) {
emit doubleTapped(point, button);
switch (m_exclusiveSignals) {
case NotExclusive:
if (m_tapCount == 1)
emit singleTapped(point, button);
else if (m_tapCount == 2)
emit doubleTapped(point, button);
break;
case SingleTap:
if (m_tapCount == 1)
emit singleTapped(point, button);
break;
case DoubleTap:
if (m_tapCount == 2)
emit doubleTapped(point, button);
break;
case (SingleTap | DoubleTap):
if (m_tapCount == 1) {
qCDebug(lcTapHandler) << objectName() << "waiting to emit singleTapped:" << m_multiTapInterval << "ms";
m_doubleTapTimer.start(m_multiTapInterval, this);
}
}
qCDebug(lcTapHandler) << objectName() << "tap" << m_tapCount << "after" << event->timestamp() - m_lastTapTimestamp << "ms";
m_lastTapTimestamp = ts;
m_lastTapPos = point.scenePosition();
} else {

View File

@ -98,7 +98,7 @@ private:
private:
QPointF m_lastTapPos;
qreal m_lastTapTimestamp = 0;
quint64 m_lastTapTimestamp = 0;
QElapsedTimer m_holdTimer;
QBasicTimer m_longPressTimer;
QBasicTimer m_doubleTapTimer;
@ -110,7 +110,7 @@ private:
ExclusiveSignals m_exclusiveSignals = NotExclusive;
bool m_pressed = false;
static qreal m_multiTapInterval;
static quint64 m_multiTapInterval;
static int m_mouseMultiClickDistanceSquared;
static int m_touchMultiTapDistanceSquared;
};

View File

@ -715,12 +715,13 @@ void tst_TapHandler::singleTapDoubleTap()
QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped);
QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped);
auto tap = [window, tapHandler, deviceType, this](const QPoint &p1) {
auto tap = [window, tapHandler, deviceType, this](const QPoint &p1, int delay = 10) {
switch (static_cast<QPointingDevice::DeviceType>(deviceType)) {
case QPointingDevice::DeviceType::Mouse:
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10);
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, delay);
break;
case QPointingDevice::DeviceType::TouchScreen:
QTest::qWait(delay);
QTest::touchEvent(window, touchDevice).press(0, p1, window);
QTRY_VERIFY(tapHandler->isPressed());
QTest::touchEvent(window, touchDevice).release(0, p1, window);
@ -741,6 +742,23 @@ void tst_TapHandler::singleTapDoubleTap()
QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount);
QCOMPARE(tappedSpy.size(), 2);
QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount);
// wait past the double-tap interval, then do it again
const auto delay = qApp->styleHints()->mouseDoubleClickInterval() + 10;
tappedSpy.clear();
singleTapSpy.clear();
doubleTapSpy.clear();
// tap once with delay
tap(p1, delay);
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()