qtdeclarative/tests/auto/quick/touchmouse/tst_touchmouse.cpp

1586 lines
62 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QDebug>
#include <QtGui/qstylehints.h>
#include <private/qdebug_p.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquickmultipointtoucharea_p.h>
#include <QtQuick/private/qquickpincharea_p.h>
#include <QtQuick/private/qquickflickable_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlproperty.h>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
struct Event
{
Event(QEvent::Type t, QPoint mouse, QPoint global)
:type(t), mousePos(mouse), mousePosGlobal(global)
{}
Event(QEvent::Type t, QList<QEventPoint> touch)
:type(t), points(touch)
{}
QEvent::Type type;
QPoint mousePos;
QPoint mousePosGlobal;
QList<QEventPoint> points;
};
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const struct Event &event) {
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "Event(";
QtDebugUtils::formatQEnum(dbg, event.type);
if (event.points.isEmpty())
dbg << " @ " << event.mousePos << " global " << event.mousePosGlobal;
else
dbg << ", " << event.points.count() << " touchpoints: " << event.points;
dbg << ')';
return dbg;
}
#endif
class EventItem : public QQuickItem
{
Q_OBJECT
Q_SIGNALS:
void onTouchEvent(QQuickItem *receiver);
public:
EventItem(QQuickItem *parent = nullptr)
: QQuickItem(parent)
{
setAcceptedMouseButtons(Qt::LeftButton);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
setAcceptTouchEvents(true);
#endif
}
void touchEvent(QTouchEvent *event)
{
eventList.append(Event(event->type(), event->touchPoints()));
QList<QEventPoint> tps = event->touchPoints();
Q_ASSERT(!tps.isEmpty());
point0 = tps.first().id();
event->setAccepted(acceptTouch);
emit onTouchEvent(this);
}
void mousePressEvent(QMouseEvent *event)
{
eventList.append(Event(event->type(), event->position().toPoint(), event->globalPosition().toPoint()));
event->setAccepted(acceptMouse);
}
void mouseMoveEvent(QMouseEvent *event)
{
eventList.append(Event(event->type(), event->position().toPoint(), event->globalPosition().toPoint()));
event->setAccepted(acceptMouse);
}
void mouseReleaseEvent(QMouseEvent *event)
{
eventList.append(Event(event->type(), event->position().toPoint(), event->globalPosition().toPoint()));
event->setAccepted(acceptMouse);
}
void mouseDoubleClickEvent(QMouseEvent *event)
{
eventList.append(Event(event->type(), event->position().toPoint(), event->globalPosition().toPoint()));
event->setAccepted(acceptMouse);
}
void mouseUngrabEvent()
{
eventList.append(Event(QEvent::UngrabMouse, QPoint(0,0), QPoint(0,0)));
}
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
void touchUngrabEvent()
{
++touchUngrabCount;
}
bool event(QEvent *event) {
return QQuickItem::event(event);
}
QList<Event> eventList;
int touchUngrabCount = 0;
bool acceptMouse = false;
bool acceptTouch = false;
bool filterTouch = false; // when used as event filter
bool eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::TouchBegin ||
event->type() == QEvent::TouchUpdate ||
event->type() == QEvent::TouchCancel ||
event->type() == QEvent::TouchEnd) {
QTouchEvent *touch = static_cast<QTouchEvent*>(event);
eventList.append(Event(event->type(), touch->touchPoints()));
QList<QEventPoint> tps = touch->touchPoints();
Q_ASSERT(!tps.isEmpty());
point0 = tps.first().id();
if (filterTouch)
event->accept();
return true;
}
return false;
}
int point0 = -1;
};
class tst_TouchMouse : public QQmlDataTest
{
Q_OBJECT
public:
tst_TouchMouse()
{}
private slots:
void initTestCase();
void simpleTouchEvent_data();
void simpleTouchEvent();
void testEventFilter();
void mouse();
void touchOverMouse();
void mouseOverTouch();
void buttonOnFlickable();
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
void touchButtonOnFlickable();
void buttonOnDelayedPressFlickable_data();
void buttonOnDelayedPressFlickable();
void buttonOnTouch();
void pinchOnFlickable();
void flickableOnPinch();
void mouseOnFlickableOnPinch();
void tapOnDismissiveTopMouseAreaClicksBottomOne();
void touchGrabCausesMouseUngrab();
void touchPointDeliveryOrder();
void hoverEnabled();
void implicitUngrab();
protected:
bool eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::MouseMove ||
event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *me = static_cast<QMouseEvent*>(event);
filteredEventList.append(Event(me->type(), me->position().toPoint(), me->globalPosition().toPoint()));
}
return false;
}
private:
QQuickView *createView();
QPointingDevice *device = QTest::createTouchDevice();
QList<Event> filteredEventList;
};
QQuickView *tst_TouchMouse::createView()
{
QQuickView *window = new QQuickView(nullptr);
return window;
}
void tst_TouchMouse::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<EventItem>("Qt.test", 1, 0, "EventItem");
}
void tst_TouchMouse::simpleTouchEvent_data()
{
QTest::addColumn<bool>("synthMouse"); // AA_SynthesizeMouseForUnhandledTouchEvents
QTest::newRow("no synth") << false;
QTest::newRow("synth") << true;
}
void tst_TouchMouse::simpleTouchEvent()
{
QFETCH(bool, synthMouse);
qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, synthMouse);
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("singleitem.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
QVERIFY(eventItem1);
// Do not accept touch or mouse
QPoint p1;
p1 = QPoint(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
// Get a touch and then mouse event offered
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
p1 += QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
// Not accepted, no updates
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1);
eventItem1->eventList.clear();
// Accept touch
eventItem1->acceptTouch = true;
p1 = QPoint(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 1);
p1 += QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 2);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 3);
eventItem1->eventList.clear();
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// Accept mouse
eventItem1->acceptTouch = false;
eventItem1->acceptMouse = true;
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
p1 = QPoint(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
if (synthMouse)
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
QCOMPARE(window->mouseGrabberItem(), synthMouse ? eventItem1 : nullptr);
QPoint localPos = eventItem1->mapFromScene(p1).toPoint();
QPoint globalPos = window->mapToGlobal(p1);
QPoint scenePos = p1; // item is at 0,0
QCOMPARE(eventItem1->eventList.at(0).points.at(0).position().toPoint(), localPos);
QCOMPARE(eventItem1->eventList.at(0).points.at(0).scenePosition().toPoint(), scenePos);
QCOMPARE(eventItem1->eventList.at(0).points.at(0).globalPosition().toPoint(), globalPos);
if (synthMouse) {
QCOMPARE(eventItem1->eventList.at(1).mousePos, localPos);
QCOMPARE(eventItem1->eventList.at(1).mousePosGlobal, globalPos);
}
p1 += QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 4 : 1);
if (synthMouse) {
QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate);
QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove);
}
// else, if there was no synth-mouse and we didn't accept the touch,
// TouchUpdate was not sent to eventItem1 either.
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 7 : 1);
if (synthMouse) {
QCOMPARE(eventItem1->eventList.at(4).type, QEvent::TouchEnd);
QCOMPARE(eventItem1->eventList.at(5).type, QEvent::MouseButtonRelease);
QCOMPARE(eventItem1->eventList.at(6).type, QEvent::UngrabMouse);
}
// else, if there was no synth-mouse and we didn't accept the touch,
// TouchEnd was not sent to eventItem1 either.
eventItem1->eventList.clear();
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// Accept mouse buttons but not the event
eventItem1->acceptTouch = false;
eventItem1->acceptMouse = false;
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
p1 = QPoint(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
if (synthMouse)
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
p1 += QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1);
eventItem1->eventList.clear();
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// Accept touch and mouse
eventItem1->acceptTouch = true;
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
p1 = QPoint(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 1);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
p1 += QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 2);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::TouchUpdate);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 3);
QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd);
eventItem1->eventList.clear();
}
void tst_TouchMouse::testEventFilter()
{
// // install event filter on item and see that it can grab events
// QScopedPointer<QQuickView> window(createView());
// window->setSource(testFileUrl("singleitem.qml"));
// window->show();
// QQuickViewTestUtil::centerOnScreen(window.data());
// QVERIFY(QTest::qWaitForWindowActive(window.data()));
// QVERIFY(window->rootObject() != 0);
// EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
// QVERIFY(eventItem1);
// eventItem1->acceptTouch = true;
// EventItem *filter = new EventItem;
// filter->filterTouch = true;
// eventItem1->installEventFilter(filter);
// QPoint p1 = QPoint(20, 20);
// QTest::touchEvent(window.data(), device).press(0, p1, window.data());
// // QEXPECT_FAIL("", "We do not implement event filters correctly", Abort);
// QCOMPARE(eventItem1->eventList.size(), 0);
// QCOMPARE(filter->eventList.size(), 1);
// QTest::touchEvent(window.data(), device).release(0, p1, window.data());
// QCOMPARE(eventItem1->eventList.size(), 0);
// QCOMPARE(filter->eventList.size(), 2);
// delete filter;
}
void tst_TouchMouse::mouse()
{
// eventItem1
// - eventItem2
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("twoitems.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
QVERIFY(eventItem1);
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
// bottom item likes mouse, top likes touch
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
eventItem1->acceptMouse = true;
// item 2 doesn't accept anything, thus it sees a touch pass by
QPoint p1 = QPoint(30, 30);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
}
void tst_TouchMouse::touchOverMouse()
{
// eventItem1
// - eventItem2
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("twoitems.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
QVERIFY(eventItem1);
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
// bottom item likes mouse, top likes touch
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
eventItem2->acceptTouch = true;
QCOMPARE(eventItem1->eventList.size(), 0);
QPoint p1 = QPoint(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 0);
QCOMPARE(eventItem2->eventList.size(), 1);
QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
p1 += QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem2->eventList.size(), 2);
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem2->eventList.size(), 3);
QCOMPARE(eventItem2->eventList.at(2).type, QEvent::TouchEnd);
eventItem2->eventList.clear();
}
void tst_TouchMouse::mouseOverTouch()
{
// eventItem1
// - eventItem2
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("twoitems.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
QVERIFY(eventItem1);
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
// bottom item likes mouse, top likes touch
eventItem1->acceptTouch = true;
eventItem2->setAcceptedMouseButtons(Qt::LeftButton);
eventItem2->acceptMouse = true;
QPoint p1 = QPoint(20, 20);
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 0);
QCOMPARE(eventItem2->eventList.size(), 2);
QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::MouseButtonPress);
// p1 += QPoint(10, 0);
// QTest::touchEvent(window.data(), device).move(0, p1, window.data());
// QCOMPARE(eventItem2->eventList.size(), 1);
// QTest::touchEvent(window.data(), device).release(0, p1, window.data());
// QCOMPARE(eventItem2->eventList.size(), 1);
// eventItem2->eventList.clear();
}
void tst_TouchMouse::buttonOnFlickable()
{
// flickable - height 500 / 1000
// - eventItem1 y: 100, height 100
// - eventItem2 y: 300, height 100
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("buttononflickable.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
// should a mouse area button be clickable on top of flickable? yes :)
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
QVERIFY(eventItem1);
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
eventItem1->acceptMouse = true;
// should a touch button be touchable on top of flickable? yes :)
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
QCOMPARE(eventItem2->eventList.size(), 0);
eventItem2->acceptTouch = true;
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// check that buttons are clickable
// mouse button
QCOMPARE(eventItem1->eventList.size(), 0);
QPoint p1 = QPoint(20, 130);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QTRY_COMPARE(eventItem1->eventList.size(), 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 5);
QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd);
QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease);
QCOMPARE(eventItem1->eventList.at(4).type, QEvent::UngrabMouse);
eventItem1->eventList.clear();
// touch button
p1 = QPoint(10, 310);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem2->eventList.size(), 1);
QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem2->eventList.size(), 2);
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchEnd);
QCOMPARE(eventItem1->eventList.size(), 0);
eventItem2->eventList.clear();
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// click above button, no events please
p1 = QPoint(10, 90);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 0);
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 0);
eventItem1->eventList.clear();
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// check that flickable moves - mouse button
QCOMPARE(eventItem1->eventList.size(), 0);
p1 = QPoint(10, 110);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data());
QVERIFY(windowPriv->touchMouseId != -1);
auto pointerEvent = windowPriv->pointerEventInstance(device);
QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), eventItem1);
QCOMPARE(window->mouseGrabberItem(), eventItem1);
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
int dragDelta = -qApp->styleHints()->startDragDistance();
p1 += QPoint(0, dragDelta);
QPoint p2 = p1 + QPoint(0, dragDelta);
QPoint p3 = p2 + QPoint(0, dragDelta);
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p2, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p3, window.data());
QQuickTouchUtils::flush(window.data());
// we cannot really know when the events get grabbed away
QVERIFY(eventItem1->eventList.size() >= 4);
QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate);
QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove);
QCOMPARE(window->mouseGrabberItem(), flickable);
QVERIFY(windowPriv->touchMouseId != -1);
QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), flickable);
QVERIFY(flickable->isMovingVertically());
QTest::touchEvent(window.data(), device).release(0, p3, window.data());
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
QQuickTouchUtils::flush(window.data());
}
void tst_TouchMouse::touchButtonOnFlickable()
{
// flickable - height 500 / 1000
// - eventItem1 y: 100, height 100
// - eventItem2 y: 300, height 100
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("buttononflickable.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
QCOMPARE(eventItem2->eventList.size(), 0);
eventItem2->acceptTouch = true;
// press via touch, then drag: check that flickable moves and that the button gets ungrabbed
QCOMPARE(eventItem2->eventList.size(), 0);
QPoint p1 = QPoint(10, 310);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem2->eventList.size(), 1);
QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data());
QVERIFY(windowPriv->touchMouseId == -1);
auto pointerEvent = windowPriv->pointerEventInstance(device);
QCOMPARE(pointerEvent->point(0)->grabberItem(), eventItem2);
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
QCOMPARE(window->mouseGrabberItem(), nullptr);
int dragDelta = qApp->styleHints()->startDragDistance() * -0.7;
p1 += QPoint(0, dragDelta);
QPoint p2 = p1 + QPoint(0, dragDelta);
QPoint p3 = p2 + QPoint(0, dragDelta);
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p2, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p3, window.data());
QQuickTouchUtils::flush(window.data());
QTRY_COMPARE(eventItem2->touchUngrabCount, 1);
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
QVERIFY(eventItem2->eventList.size() > 2);
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate);
QCOMPARE(window->mouseGrabberItem(), flickable);
QVERIFY(windowPriv->touchMouseId != -1);
QCOMPARE(pointerEvent->point(0)->grabberItem(), flickable);
When stealing a touchpoint as synth. mouse, ungrab touch If you use MultiPointTouchArea to make a button component, or if you do something similar by subclassing QQuickItem and handling touch events, and you place such a component inside a Flickable, when the user presses on the button and then drags far enough that the Flickable steals the grab, the MPTA or custom item did not receive the touchUngrabEvent() callback. Now it does, so now the button will go back to released state as a result of having the grab stolen. The situation here is special in that it's the only place where a touch event is transformed to be treated as mouse in the future, usually it's either treated as touch or mouse from the start. When this happens, it's not enough to call setMouseGrabber because that doesn't send touch cancel to the previous grabber. Instead we need to explicitly call touchUngrabEvent to notify the touch handling item. The explicit setting of the grabber which was there previously is not needed, since grabMouse will update the grab based on touchMouseId. tst_QQuickMultiPointTouchArea::inFlickable2 was already testing the pressed state of the touchpoint when the grab is stolen, but it was changed in 468626e99a90d6ac21cb311cde05c658ccb3b781; now that can be restored, and we can also un-blacklist inFlickable, which was deemed unstable in 6d163779711d4601931ae0f82910794fb2498136 [ChangeLog][QtQuick] MultiPointTouchArea, and any custom Item, will now properly receive touchUngrabEvent() when the touch grab is stolen by a filtering parent Item, such as a Flickable. Task-number: QTBUG-57910 Change-Id: I4e1b23ed099f01e7eca2e8a0e7ab4c652ef00cfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2017-01-05 15:16:43 +00:00
QVERIFY(flickable->isMovingVertically());
QTest::touchEvent(window.data(), device).release(0, p3, window.data());
QQuickTouchUtils::flush(window.data());
}
void tst_TouchMouse::buttonOnDelayedPressFlickable_data()
{
QTest::addColumn<bool>("scrollBeforeDelayIsOver");
QTest::addColumn<bool>("releaseBeforeDelayIsOver");
// the item should never see the event,
// due to the pressDelay which never delivers if we start moving
QTest::newRow("scroll before press delay is over") << true << false;
// after release, the item should see the press and release via event replay (QTBUG-61144)
QTest::newRow("release before press delay is over") << false << true;
// wait until the "button" sees the press but then
// start moving: the button gets a press and cancel event
QTest::newRow("scroll after press delay is over") << false << false;
}
void tst_TouchMouse::buttonOnDelayedPressFlickable()
{
// flickable - height 500 / 1000
// - eventItem1 y: 100, height 100
// - eventItem2 y: 300, height 100
QFETCH(bool, scrollBeforeDelayIsOver);
QFETCH(bool, releaseBeforeDelayIsOver);
#ifdef Q_OS_MACOS
QSKIP("Deadlocks or crashes due to events changes in qtbase");
#endif
qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
filteredEventList.clear();
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("buttononflickable.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
window->installEventFilter(this);
// wait 600 ms before letting the child see the press event
flickable->setPressDelay(600);
// should a mouse area button be clickable on top of flickable? yes :)
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
QVERIFY(eventItem1);
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
eventItem1->acceptMouse = true;
// should a touch button be touchable on top of flickable? yes :)
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
QCOMPARE(eventItem2->eventList.size(), 0);
eventItem2->acceptTouch = true;
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data());
QCOMPARE(windowPriv->touchMouseId, -1); // no grabber
// touch press
QPoint p1 = QPoint(10, 110);
QPoint pEnd = p1;
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
if (scrollBeforeDelayIsOver || releaseBeforeDelayIsOver) {
// no events yet: press is delayed
QCOMPARE(eventItem1->eventList.size(), 0);
} else {
// wait until the button sees the press
QTRY_COMPARE(eventItem1->eventList.size(), 1);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
QCOMPARE(filteredEventList.count(), 1);
}
if (!releaseBeforeDelayIsOver) {
// move the touchpoint: try to flick
p1 += QPoint(0, -10);
QPoint p2 = p1 + QPoint(0, -10);
pEnd = p2 + QPoint(0, -10);
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p2, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, pEnd, window.data());
QQuickTouchUtils::flush(window.data());
QTRY_VERIFY(flickable->isMovingVertically());
if (scrollBeforeDelayIsOver) {
QCOMPARE(eventItem1->eventList.size(), 0);
QCOMPARE(filteredEventList.count(), 0);
} else {
// see at least press, move and ungrab
QTRY_VERIFY(eventItem1->eventList.size() > 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
QCOMPARE(eventItem1->eventList.last().type, QEvent::UngrabMouse);
QCOMPARE(filteredEventList.count(), 1);
}
// flickable should have the mouse grab, and have moved the itemForTouchPointId
// for the touchMouseId to the new grabber.
QCOMPARE(window->mouseGrabberItem(), flickable);
QVERIFY(windowPriv->touchMouseId != -1);
auto pointerEvent = windowPriv->pointerEventInstance(device);
QCOMPARE(pointerEvent->point(0)->grabberItem(), flickable);
}
QTest::touchEvent(window.data(), device).release(0, pEnd, window.data());
QQuickTouchUtils::flush(window.data());
if (releaseBeforeDelayIsOver) {
// when the touchpoint was released, the child saw the delayed press and the release in sequence
qCDebug(lcTests) << "expected delivered events: press, release, ungrab" << eventItem1->eventList;
qCDebug(lcTests) << "expected filtered events: delayed press, release" << filteredEventList;
QSKIP("QTBUG-85607");
QTRY_COMPARE(eventItem1->eventList.size(), 3);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonRelease);
QCOMPARE(eventItem1->eventList.last().type, QEvent::UngrabMouse);
// QQuickWindow filters the delayed press and release
QCOMPARE(filteredEventList.count(), 2);
QCOMPARE(filteredEventList.at(0).type, QEvent::MouseButtonPress);
QCOMPARE(filteredEventList.at(1).type, QEvent::MouseButtonRelease);
} else {
// QQuickWindow filters the delayed press if there was one; otherwise nothing
if (scrollBeforeDelayIsOver) {
QCOMPARE(filteredEventList.count(), 0);
} else {
qCDebug(lcTests) << "expected filtered event: delayed press" << filteredEventList;
QCOMPARE(filteredEventList.count(), 1);
QCOMPARE(filteredEventList.at(0).type, QEvent::MouseButtonPress);
}
}
}
void tst_TouchMouse::buttonOnTouch()
{
// 400x800
// PinchArea - height 400
// - eventItem1 y: 100, height 100
// - eventItem2 y: 300, height 100
// MultiPointTouchArea - height 400
// - eventItem1 y: 100, height 100
// - eventItem2 y: 300, height 100
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("buttonontouch.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
QVERIFY(pinchArea);
QQuickItem *button1 = window->rootObject()->findChild<QQuickItem*>("button1");
QVERIFY(button1);
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
QVERIFY(eventItem1);
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
QQuickMultiPointTouchArea *touchArea = window->rootObject()->findChild<QQuickMultiPointTouchArea*>("toucharea");
QVERIFY(touchArea);
EventItem *eventItem3 = window->rootObject()->findChild<EventItem*>("eventItem3");
QVERIFY(eventItem3);
EventItem *eventItem4 = window->rootObject()->findChild<EventItem*>("eventItem4");
QVERIFY(eventItem4);
QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window.data(), device, false);
// Test the common case of a mouse area on top of pinch
eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
eventItem1->acceptMouse = true;
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// Normal touch click
QPoint p1 = QPoint(10, 110);
touchSeq.press(0, p1, window.data()).commit();
QQuickTouchUtils::flush(window.data());
touchSeq.release(0, p1, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem1->eventList.size(), 5);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd);
QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease);
QCOMPARE(eventItem1->eventList.at(4).type, QEvent::UngrabMouse);
eventItem1->eventList.clear();
// Normal mouse click
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, p1);
QCOMPARE(eventItem1->eventList.size(), 3);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonRelease);
QCOMPARE(eventItem1->eventList.at(2).type, QEvent::UngrabMouse);
eventItem1->eventList.clear();
// Pinch starting on the PinchArea should work
p1 = QPoint(40, 10);
QPoint p2 = QPoint(60, 10);
// Start the events after each other
touchSeq.press(0, p1, window.data()).commit();
QQuickTouchUtils::flush(window.data());
touchSeq.stationary(0).press(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(button1->scale(), 1.0);
// This event seems to be discarded, let's ignore it for now until someone digs into pincharea
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
touchSeq.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
touchSeq.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// QCOMPARE(button1->scale(), 1.5);
qDebug() << "Button scale: " << button1->scale();
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
touchSeq.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// QCOMPARE(button1->scale(), 2.0);
qDebug() << "Button scale: " << button1->scale();
touchSeq.release(0, p1, window.data()).release(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// QVERIFY(eventItem1->eventList.isEmpty());
// QCOMPARE(button1->scale(), 2.0);
qDebug() << "Button scale: " << button1->scale();
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
// Start pinching while on the button
button1->setScale(1.0);
p1 = QPoint(40, 110);
p2 = QPoint(60, 110);
touchSeq.press(0, p1, window.data()).press(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(button1->scale(), 1.0);
QCOMPARE(eventItem1->eventList.count(), 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
// This event seems to be discarded, let's ignore it for now until someone digs into pincharea
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
touchSeq.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
touchSeq.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
//QCOMPARE(button1->scale(), 1.5);
qDebug() << button1->scale();
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
touchSeq.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
qDebug() << button1->scale();
//QCOMPARE(button1->scale(), 2.0);
touchSeq.release(0, p1, window.data()).release(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// QCOMPARE(eventItem1->eventList.size(), 99);
qDebug() << button1->scale();
//QCOMPARE(button1->scale(), 2.0);
}
void tst_TouchMouse::pinchOnFlickable()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pinchonflickable.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
QVERIFY(pinchArea);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>("rect");
QVERIFY(rect);
// flickable - single touch point
QCOMPARE(flickable->contentX(), 0.0);
QPoint p = QPoint(100, 100);
QTest::touchEvent(window.data(), device).press(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->position(), QPointF(200.0, 200.0));
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).release(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QGuiApplication::processEvents();
QTest::qWait(10);
QVERIFY(!flickable->isAtXBeginning());
// wait until flicking is done
QTRY_VERIFY(!flickable->isFlicking());
// pinch
QPoint p1 = QPoint(40, 20);
QPoint p2 = QPoint(60, 20);
QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window.data(), device);
QQuickTouchUtils::flush(window.data());
pinchSequence.press(0, p1, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// In order for the stationary point to remember its previous position,
// we have to reuse the same pinchSequence object. Otherwise if we let it
// be destroyed and then start a new sequence, point 0 will default to being
// stationary at 0, 0, and PinchArea will filter out that touchpoint because
// it is outside its bounds.
pinchSequence.stationary(0).press(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10,10);
p2 += QPoint(10,10);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->scale(), 1.0);
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
Fix touch/mouse propagation bugs Filtered mouse release was not delivered if another touch started after a touchMouseId was activated. This meant that any filters expecting a release event would not receive it if another touch was made before release of the touchMouseId. We prevented a touch becoming the touchMouseId in the child mouse filters if there were any existing touches. The normal event delivery, however, does not require a single touch. Further to the previous, a touch could become the touchMouseId, even if the initial press happened when there was an existing touchMouseId. This meant that a touch could turn into a mouse when the existing mouse event was released, resulting in a new touchMouseId which hadn't been through child mouse filters. Flickable delayed press should be sent via normal event processing, as other touch/mouse events are now delivered in this way. We often called childMouseEventFilter() multiple times for each event. This is bad because the gesture handling relies on claiming a gesture in one event, then stealing it in the next. Instead of sending touch to mouse candidate points already determined to be within the item bounds and already transformed, we sent all of the points to the mouse recipient. PinchArea did not store the starting position at the original touch points, so other items could pass the dragThreshold before PinchArea and steal a gesture meant for PinchArea. Task-number: QTBUG-40330 Change-Id: Ic0009c176d3d1cb7cff0b5eda076a2c3ca864136 Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
2014-07-22 01:31:04 +00:00
QVERIFY(!flickable->isDragging());
QQuickTouchUtils::flush(window.data());
pinchSequence.release(0, p1, window.data()).release(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QVERIFY(rect->scale() > 1.0);
}
void tst_TouchMouse::flickableOnPinch()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("flickableonpinch.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
QVERIFY(pinchArea);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>("rect");
QVERIFY(rect);
// flickable - single touch point
QCOMPARE(flickable->contentX(), 0.0);
QPoint p = QPoint(100, 100);
QTest::touchEvent(window.data(), device).press(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->position(), QPointF(200.0, 200.0));
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QTest::qWait(1000);
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).release(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QTest::qWait(1000);
//QVERIFY(flickable->isMovingHorizontally());
qDebug() << "Pos: " << rect->position();
// wait until flicking is done
QTRY_VERIFY(!flickable->isFlicking());
// pinch
QPoint p1 = QPoint(40, 20);
QPoint p2 = QPoint(60, 20);
QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window.data(), device);
pinchSequence.press(0, p1, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// In order for the stationary point to remember its previous position,
// we have to reuse the same pinchSequence object. Otherwise if we let it
// be destroyed and then start a new sequence, point 0 will default to being
// stationary at 0, 0, and PinchArea will filter out that touchpoint because
// it is outside its bounds.
pinchSequence.stationary(0).press(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10,10);
p2 += QPoint(10,10);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->scale(), 1.0);
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
pinchSequence.release(0, p1, window.data()).release(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QVERIFY(rect->scale() > 1.0);
}
void tst_TouchMouse::mouseOnFlickableOnPinch()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("mouseonflickableonpinch.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
QRect windowRect = QRect(window->position(), window->size());
QCursor::setPos(windowRect.center());
QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
QVERIFY(pinchArea);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>("rect");
QVERIFY(rect);
// flickable - single touch point
QCOMPARE(flickable->contentX(), 0.0);
QPoint p = QPoint(100, 100);
QTest::touchEvent(window.data(), device).press(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->position(), QPointF(200.0, 200.0));
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
p -= QPoint(10, 0);
QTest::touchEvent(window.data(), device).move(0, p, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).release(0, p, window.data());
QQuickTouchUtils::flush(window.data());
//QVERIFY(flickable->isMovingHorizontally());
// Wait for flick to end
QTRY_VERIFY(!flickable->isMoving());
qDebug() << "Pos: " << rect->position();
// pinch
QPoint p1 = QPoint(40, 20);
QPoint p2 = QPoint(60, 20);
QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window.data(), device);
pinchSequence.press(0, p1, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// In order for the stationary point to remember its previous position,
// we have to reuse the same pinchSequence object. Otherwise if we let it
// be destroyed and then start a new sequence, point 0 will default to being
// stationary at 0, 0, and PinchArea will filter out that touchpoint because
// it is outside its bounds.
pinchSequence.stationary(0).press(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10,10);
p2 += QPoint(10,10);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->scale(), 1.0);
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(10, 0);
p2 += QPoint(10, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
pinchSequence.release(0, p1, window.data()).release(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QVERIFY(rect->scale() > 1.0);
// PinchArea should steal the event after flicking started
rect->setScale(1.0);
flickable->setContentX(0.0);
p = QPoint(100, 100);
pinchSequence.press(0, p, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->position(), QPointF(200.0, 200.0));
p -= QPoint(10, 0);
pinchSequence.move(0, p, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p -= QPoint(10, 0);
pinchSequence.move(0, p, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QGuiApplication::processEvents();
p -= QPoint(10, 0);
pinchSequence.move(0, p, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(window->mouseGrabberItem(), flickable);
// Add a second finger, this should lead to stealing
p1 = QPoint(40, 100);
p2 = QPoint(60, 100);
pinchSequence.stationary(0).press(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(rect->scale(), 1.0);
p1 -= QPoint(5, 0);
p2 += QPoint(5, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(5, 0);
p2 += QPoint(5, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
p1 -= QPoint(5, 0);
p2 += QPoint(5, 0);
pinchSequence.move(0, p1, window.data()).move(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
pinchSequence.release(0, p1, window.data()).release(1, p2, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QVERIFY(rect->scale() > 1.0);
pinchSequence.release(0, p, window.data()).commit();
QQuickTouchUtils::flush(window.data());
}
/*
Regression test for the following use case:
You have two mouse areas, on on top of the other.
1 - You tap the top one.
2 - That top mouse area receives a mouse press event but doesn't accept it
Expected outcome:
3 - the bottom mouse area gets clicked (besides press and release mouse events)
Bogus outcome:
3 - the bottom mouse area gets double clicked.
*/
void tst_TouchMouse::tapOnDismissiveTopMouseAreaClicksBottomOne()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("twoMouseAreas.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
QQuickMouseArea *bottomMouseArea =
window->rootObject()->findChild<QQuickMouseArea*>("rear mouseArea");
QSignalSpy bottomClickedSpy(bottomMouseArea, SIGNAL(clicked(QQuickMouseEvent*)));
QSignalSpy bottomDoubleClickedSpy(bottomMouseArea,
SIGNAL(doubleClicked(QQuickMouseEvent*)));
// tap the front mouse area (see qml file)
QPoint p1(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(bottomClickedSpy.count(), 1);
QCOMPARE(bottomDoubleClickedSpy.count(), 0);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(bottomClickedSpy.count(), 1);
QCOMPARE(bottomDoubleClickedSpy.count(), 1);
}
/*
If an item grabs a touch that is currently being used for mouse pointer emulation,
the current mouse grabber should lose the mouse as mouse events will no longer
be generated from that touch point.
*/
void tst_TouchMouse::touchGrabCausesMouseUngrab()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("twosiblingitems.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != nullptr);
EventItem *leftItem = window->rootObject()->findChild<EventItem*>("leftItem");
QVERIFY(leftItem);
EventItem *rightItem = window->rootObject()->findChild<EventItem*>("rightItem");
QVERIFY(leftItem);
// Send a touch to the leftItem. But leftItem accepts only mouse events, thus
// a mouse event will be synthesized out of this touch and will get accepted by
// leftItem.
leftItem->acceptMouse = true;
leftItem->setAcceptedMouseButtons(Qt::LeftButton);
QPoint p1;
p1 = QPoint(leftItem->width() / 2, leftItem->height() / 2);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(leftItem->eventList.size(), 2);
QCOMPARE(leftItem->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(leftItem->eventList.at(1).type, QEvent::MouseButtonPress);
QCOMPARE(window->mouseGrabberItem(), leftItem);
leftItem->eventList.clear();
rightItem->acceptTouch = true;
{
QList<int> ids;
ids.append(leftItem->point0);
rightItem->grabTouchPoints(ids);
}
// leftItem should have lost the mouse as the touch point that was being used to emulate it
// has been grabbed by another item.
QCOMPARE(leftItem->eventList.size(), 1);
QCOMPARE(leftItem->eventList.at(0).type, QEvent::UngrabMouse);
QCOMPARE(window->mouseGrabberItem(), (QQuickItem*)nullptr);
}
void tst_TouchMouse::touchPointDeliveryOrder()
{
// Touch points should be first delivered to the item under the primary finger
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("touchpointdeliveryorder.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
/*
The items are positioned from left to right:
| background |
| left |
| | right |
| middle |
0 150 300 450 600
*/
QPoint pLeft = QPoint(100, 100);
QPoint pRight = QPoint(500, 100);
QPoint pLeftMiddle = QPoint(200, 100);
QPoint pRightMiddle = QPoint(350, 100);
QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window.data(), device, false);
QVector<QQuickItem*> events;
EventItem *background = window->rootObject()->findChild<EventItem*>("background");
EventItem *left = window->rootObject()->findChild<EventItem*>("left");
EventItem *middle = window->rootObject()->findChild<EventItem*>("middle");
EventItem *right = window->rootObject()->findChild<EventItem*>("right");
QVERIFY(background);
QVERIFY(left);
QVERIFY(middle);
QVERIFY(right);
connect(background, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
connect(left, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
connect(middle, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
connect(right, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
touchSeq.press(0, pLeft, window.data()).commit();
QQuickTouchUtils::flush(window.data());
// Touch on left, then background
QCOMPARE(events.size(), 2);
QCOMPARE(events.at(0), left);
QCOMPARE(events.at(1), background);
events.clear();
// New press events are deliverd first, the stationary point was not accepted, thus it doesn't get delivered
touchSeq.stationary(0).press(1, pRightMiddle, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(events.size(), 3);
QCOMPARE(events.at(0), middle);
QCOMPARE(events.at(1), right);
QCOMPARE(events.at(2), background);
events.clear();
touchSeq.release(0, pLeft, window.data()).release(1, pRightMiddle, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(events.size(), 0); // no accepted events
// Two presses, the first point should come first
touchSeq.press(0, pLeft, window.data()).press(1, pRight, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(events.size(), 3);
QCOMPARE(events.at(0), left);
QCOMPARE(events.at(1), right);
QCOMPARE(events.at(2), background);
touchSeq.release(0, pLeft, window.data()).release(1, pRight, window.data()).commit();
events.clear();
// Again, pressing right first
touchSeq.press(0, pRight, window.data()).press(1, pLeft, window.data()).commit();
QQuickTouchUtils::flush(window.data());
QCOMPARE(events.size(), 3);
QCOMPARE(events.at(0), right);
QCOMPARE(events.at(1), left);
QCOMPARE(events.at(2), background);
touchSeq.release(0, pRight, window.data()).release(1, pLeft, window.data()).commit();
events.clear();
// Two presses, both hitting the middle item on top, then branching left and right, then bottom
// Each target should be offered the events exactly once, middle first, left must come before right (id 0)
touchSeq.press(0, pLeftMiddle, window.data()).press(1, pRightMiddle, window.data()).commit();
QCOMPARE(events.size(), 4);
QCOMPARE(events.at(0), middle);
QCOMPARE(events.at(1), left);
QCOMPARE(events.at(2), right);
QCOMPARE(events.at(3), background);
touchSeq.release(0, pLeftMiddle, window.data()).release(1, pRightMiddle, window.data()).commit();
events.clear();
touchSeq.press(0, pRightMiddle, window.data()).press(1, pLeftMiddle, window.data()).commit();
qDebug() << events;
QCOMPARE(events.size(), 4);
QCOMPARE(events.at(0), middle);
QCOMPARE(events.at(1), right);
QCOMPARE(events.at(2), left);
QCOMPARE(events.at(3), background);
touchSeq.release(0, pRightMiddle, window.data()).release(1, pLeftMiddle, window.data()).commit();
}
void tst_TouchMouse::hoverEnabled()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("hoverMouseAreas.qml"));
QQuickViewTestUtil::centerOnScreen(window.data());
QQuickViewTestUtil::moveMouseAway(window.data());
window->show();
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QQuickItem *root = window->rootObject();
QVERIFY(root != nullptr);
QQuickMouseArea *mouseArea1 = root->findChild<QQuickMouseArea*>("mouseArea1");
QVERIFY(mouseArea1 != nullptr);
QQuickMouseArea *mouseArea2 = root->findChild<QQuickMouseArea*>("mouseArea2");
QVERIFY(mouseArea2 != nullptr);
QSignalSpy enterSpy1(mouseArea1, SIGNAL(entered()));
QSignalSpy exitSpy1(mouseArea1, SIGNAL(exited()));
QSignalSpy clickSpy1(mouseArea1, SIGNAL(clicked(QQuickMouseEvent *)));
QSignalSpy enterSpy2(mouseArea2, SIGNAL(entered()));
QSignalSpy exitSpy2(mouseArea2, SIGNAL(exited()));
QSignalSpy clickSpy2(mouseArea2, SIGNAL(clicked(QQuickMouseEvent *)));
QPoint p1(150, 150);
QPoint p2(150, 250);
// ------------------------- Mouse move to mouseArea1
QTest::mouseMove(window.data(), p1);
QVERIFY(enterSpy1.count() == 1);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
// ------------------------- Touch click on mouseArea1
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QCOMPARE(enterSpy1.count(), 1);
QCOMPARE(enterSpy2.count(), 0);
QVERIFY(mouseArea1->pressed());
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QVERIFY(clickSpy1.count() == 1);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
// ------------------------- Touch click on mouseArea2
QTest::touchEvent(window.data(), device).press(0, p2, window.data());
QVERIFY(mouseArea1->hovered());
QVERIFY(mouseArea2->hovered());
QVERIFY(mouseArea2->pressed());
QCOMPARE(enterSpy1.count(), 1);
QCOMPARE(enterSpy2.count(), 1);
QTest::touchEvent(window.data(), device).release(0, p2, window.data());
QVERIFY(clickSpy2.count() == 1);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
QCOMPARE(exitSpy1.count(), 0);
QCOMPARE(exitSpy2.count(), 1);
// ------------------------- Another touch click on mouseArea1
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QCOMPARE(enterSpy1.count(), 1);
QCOMPARE(enterSpy2.count(), 1);
QVERIFY(mouseArea1->pressed());
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
QTest::touchEvent(window.data(), device).release(0, p1, window.data());
QCOMPARE(clickSpy1.count(), 2);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea1->pressed());
QVERIFY(!mouseArea2->hovered());
}
void tst_TouchMouse::implicitUngrab()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("singleitem.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QQuickViewTestUtil::moveMouseAway(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QQuickItem *root = window->rootObject();
QVERIFY(root != nullptr);
EventItem *eventItem = root->findChild<EventItem*>("eventItem1");
eventItem->acceptMouse = true;
QPoint p1(20, 20);
QTest::touchEvent(window.data(), device).press(0, p1);
QCOMPARE(window->mouseGrabberItem(), eventItem);
eventItem->eventList.clear();
eventItem->setEnabled(false);
QVERIFY(!eventItem->eventList.isEmpty());
QCOMPARE(eventItem->eventList.at(0).type, QEvent::UngrabMouse);
QTest::touchEvent(window.data(), device).release(0, p1); // clean up potential state
eventItem->setEnabled(true);
QTest::touchEvent(window.data(), device).press(0, p1);
eventItem->eventList.clear();
eventItem->setVisible(false);
QVERIFY(!eventItem->eventList.isEmpty());
QCOMPARE(eventItem->eventList.at(0).type, QEvent::UngrabMouse);
QTest::touchEvent(window.data(), device).release(0, p1); // clean up potential state
}
QTEST_MAIN(tst_TouchMouse)
#include "tst_touchmouse.moc"