qtdeclarative/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp

2630 lines
96 KiB
C++
Raw Normal View History

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#include <QtQuick/private/qquickdrag_p.h>
#include <QtQuick/private/qquickitem_p.h>
Say hello to QtQuick module This change moves the QtQuick 2 types and C++ API (including SceneGraph) to a new module (AKA library), QtQuick. 99% of this change is moving files from src/declarative to src/quick, and from tests/auto/declarative to tests/auto/qtquick2. The loading of QtQuick 2 ("import QtQuick 2.0") is now delegated to a plugin, src/imports/qtquick2, just like it's done for QtQuick 1. All tools, examples, and tests that use QtQuick C++ API have gotten "QT += quick" or "QT += quick-private" added to their .pro file. A few additional internal QtDeclarative classes had to be exported (via Q_DECLARATIVE_PRIVATE_EXPORT) since they're needed by the QtQuick 2 implementation. The old header locations (e.g. QtDeclarative/qquickitem.h) will still be supported for some time, but will produce compile-time warnings. (To avoid the QtQuick implementation using the compatibility headers (since QtDeclarative's includepath comes first), a few include statements were modified, e.g. from "#include <qsgnode.h>" to "#include <QtQuick/qsgnode.h>".) There's a change in qtbase that automatically adds QtQuick to the module list if QtDeclarative is used. Together with the compatibility headers, this should help reduce the migration pain for existing projects. In theory, simply getting an existing QtDeclarative-based project to compile and link shouldn't require any changes for now -- but porting to the new scheme is of course recommended, and will eventually become mandatory. Task-number: QTBUG-22889 Reviewed-by: Lars Knoll <lars.knoll@nokia.com> Change-Id: Ia52be9373172ba2f37e7623231ecb060316c96a7 Reviewed-by: Kent Hansen <kent.hansen@nokia.com> Reviewed-by: Sergio Ahumada <sergio.ahumada@nokia.com>
2011-11-23 14:14:07 +00:00
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <private/qquickflickable_p.h>
Say hello to QtQuick module This change moves the QtQuick 2 types and C++ API (including SceneGraph) to a new module (AKA library), QtQuick. 99% of this change is moving files from src/declarative to src/quick, and from tests/auto/declarative to tests/auto/qtquick2. The loading of QtQuick 2 ("import QtQuick 2.0") is now delegated to a plugin, src/imports/qtquick2, just like it's done for QtQuick 1. All tools, examples, and tests that use QtQuick C++ API have gotten "QT += quick" or "QT += quick-private" added to their .pro file. A few additional internal QtDeclarative classes had to be exported (via Q_DECLARATIVE_PRIVATE_EXPORT) since they're needed by the QtQuick 2 implementation. The old header locations (e.g. QtDeclarative/qquickitem.h) will still be supported for some time, but will produce compile-time warnings. (To avoid the QtQuick implementation using the compatibility headers (since QtDeclarative's includepath comes first), a few include statements were modified, e.g. from "#include <qsgnode.h>" to "#include <QtQuick/qsgnode.h>".) There's a change in qtbase that automatically adds QtQuick to the module list if QtDeclarative is used. Together with the compatibility headers, this should help reduce the migration pain for existing projects. In theory, simply getting an existing QtDeclarative-based project to compile and link shouldn't require any changes for now -- but porting to the new scheme is of course recommended, and will eventually become mandatory. Task-number: QTBUG-22889 Reviewed-by: Lars Knoll <lars.knoll@nokia.com> Change-Id: Ia52be9373172ba2f37e7623231ecb060316c96a7 Reviewed-by: Kent Hansen <kent.hansen@nokia.com> Reviewed-by: Sergio Ahumada <sergio.ahumada@nokia.com>
2011-11-23 14:14:07 +00:00
#include <QtQuick/qquickview.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlengine.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtGui/qstylehints.h>
#include <QtGui/QCursor>
#include <QtGui/QScreen>
#include <QEvent>
#include <QQmlComponent>
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qwindowsysteminterface_p.h>
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
class CircleMask : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
public:
virtual ~CircleMask() {}
qreal radius() const { return m_radius; }
void setRadius(qreal radius)
{
if (m_radius == radius)
return;
m_radius = radius;
emit radiusChanged();
}
Q_INVOKABLE bool contains(const QPointF &point) const
{
QPointF center(m_radius, m_radius);
QLineF line(center, point);
return line.length() <= m_radius;
}
signals:
void radiusChanged();
private:
qreal m_radius;
};
class EventSender : public QObject {
Q_OBJECT
public:
Q_INVOKABLE void sendMouseClick(QWindow* w ,qreal x , qreal y) {
QTest::mouseClick(w, Qt::LeftButton, {}, QPointF(x, y).toPoint());
}
};
class tst_QQuickMouseArea: public QQmlDataTest
{
Q_OBJECT
public:
tst_QQuickMouseArea()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
qmlRegisterType<CircleMask>("Test", 1, 0, "CircleMask");
qmlRegisterType<EventSender>("Test", 1, 0, "EventSender");
}
private slots:
void dragProperties();
void resetDrag();
void dragging_data() { acceptedButton_data(); }
void dragging();
void selfDrag();
void dragSmoothed();
void dragThreshold_data();
void dragThreshold();
void invalidDrag_data() { rejectedButton_data(); }
void invalidDrag();
void cancelDragging();
void availableDistanceLessThanDragThreshold();
void setDragOnPressed();
void updateMouseAreaPosOnClick();
void updateMouseAreaPosOnResize();
void noOnClickedWithPressAndHold();
void onMousePressRejected();
void pressedCanceledOnWindowDeactivate_data();
void pressedCanceledOnWindowDeactivate();
void doubleClick_data() { acceptedButton_data(); }
void doubleClick();
void clickTwice_data() { acceptedButton_data(); }
void clickTwice();
void invalidClick_data() { rejectedButton_data(); }
void invalidClick();
void pressedOrdering();
void preventStealing();
void preventStealingListViewChild();
void clickThrough();
void hoverPosition();
void hoverPropagation();
void hoverVisible();
void hoverAfterPress();
void subtreeHoverEnabled();
void hoverWhenDisabled();
void disableAfterPress();
void disableParentOnPress_data();
void disableParentOnPress();
void onWheel();
void transformedMouseArea_data();
void transformedMouseArea();
void pressedMultipleButtons_data();
void pressedMultipleButtons();
void changeAxis();
#if QT_CONFIG(cursor)
void cursorShape();
#endif
void moveAndReleaseWithoutPress();
void nestedStopAtBounds();
void nestedStopAtBounds_data();
void nestedFlickableStopAtBounds();
void containsPress_data();
void containsPress();
void ignoreBySource();
void notPressedAfterStolenGrab();
void pressAndHold_data();
void pressAndHold();
void pressOneAndTapAnother_data();
void pressOneAndTapAnother();
void mask();
void nestedEventDelivery();
void settingHiddenInPressUngrabs();
void negativeZStackingOrder();
void containsMouseAndVisibility();
void containsMouseAndVisibilityMasked();
void containsMouseAndHoverDisabled();
void doubleClickToHide();
void releaseFirstTouchAfterSecond();
#if QT_CONFIG(tabletevent)
void tabletStylusTap();
#endif
void syntheticRightClick();
private:
int startDragDistance() const {
return QGuiApplication::styleHints()->startDragDistance();
}
void acceptedButton_data();
void rejectedButton_data();
QPointingDevice *device = QTest::createTouchDevice(); // TODO const after fixing QTBUG-107864
};
Q_DECLARE_METATYPE(Qt::MouseButton)
Q_DECLARE_METATYPE(Qt::MouseButtons)
void tst_QQuickMouseArea::acceptedButton_data()
{
QTest::addColumn<Qt::MouseButtons>("acceptedButtons");
QTest::addColumn<Qt::MouseButton>("button");
QTest::newRow("left") << Qt::MouseButtons(Qt::LeftButton) << Qt::LeftButton;
QTest::newRow("right") << Qt::MouseButtons(Qt::RightButton) << Qt::RightButton;
QTest::newRow("middle") << Qt::MouseButtons(Qt::MiddleButton) << Qt::MiddleButton;
QTest::newRow("left (left|right)") << Qt::MouseButtons(Qt::LeftButton | Qt::RightButton) << Qt::LeftButton;
QTest::newRow("right (right|middle)") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::RightButton;
QTest::newRow("middle (left|middle)") << Qt::MouseButtons(Qt::LeftButton | Qt::MiddleButton) << Qt::MiddleButton;
}
void tst_QQuickMouseArea::rejectedButton_data()
{
QTest::addColumn<Qt::MouseButtons>("acceptedButtons");
QTest::addColumn<Qt::MouseButton>("button");
QTest::newRow("middle (left|right)") << Qt::MouseButtons(Qt::LeftButton | Qt::RightButton) << Qt::MiddleButton;
QTest::newRow("left (right|middle)") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::LeftButton;
QTest::newRow("right (left|middle)") << Qt::MouseButtons(Qt::LeftButton | Qt::MiddleButton) << Qt::RightButton;
}
void tst_QQuickMouseArea::dragProperties()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("dragproperties.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseRegion->drag();
QVERIFY(mouseRegion != nullptr);
QVERIFY(drag != nullptr);
// target
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QQuickItem *rootItem = qobject_cast<QQuickItem*>(window.rootObject());
QVERIFY(rootItem != nullptr);
QSignalSpy targetSpy(drag, SIGNAL(targetChanged()));
drag->setTarget(rootItem);
QCOMPARE(targetSpy.size(),1);
drag->setTarget(rootItem);
QCOMPARE(targetSpy.size(),1);
// axis
QCOMPARE(drag->axis(), QQuickDrag::XAndYAxis);
QSignalSpy axisSpy(drag, SIGNAL(axisChanged()));
drag->setAxis(QQuickDrag::XAxis);
QCOMPARE(drag->axis(), QQuickDrag::XAxis);
QCOMPARE(axisSpy.size(),1);
drag->setAxis(QQuickDrag::XAxis);
QCOMPARE(axisSpy.size(),1);
// minimum and maximum properties
QSignalSpy xminSpy(drag, SIGNAL(minimumXChanged()));
QSignalSpy xmaxSpy(drag, SIGNAL(maximumXChanged()));
QSignalSpy yminSpy(drag, SIGNAL(minimumYChanged()));
QSignalSpy ymaxSpy(drag, SIGNAL(maximumYChanged()));
QCOMPARE(drag->xmin(), 0.0);
QCOMPARE(drag->xmax(), rootItem->width()-blackRect->width());
QCOMPARE(drag->ymin(), 0.0);
QCOMPARE(drag->ymax(), rootItem->height()-blackRect->height());
drag->setXmin(10);
drag->setXmax(10);
drag->setYmin(10);
drag->setYmax(10);
QCOMPARE(drag->xmin(), 10.0);
QCOMPARE(drag->xmax(), 10.0);
QCOMPARE(drag->ymin(), 10.0);
QCOMPARE(drag->ymax(), 10.0);
QCOMPARE(xminSpy.size(),1);
QCOMPARE(xmaxSpy.size(),1);
QCOMPARE(yminSpy.size(),1);
QCOMPARE(ymaxSpy.size(),1);
drag->setXmin(10);
drag->setXmax(10);
drag->setYmin(10);
drag->setYmax(10);
QCOMPARE(xminSpy.size(),1);
QCOMPARE(xmaxSpy.size(),1);
QCOMPARE(yminSpy.size(),1);
QCOMPARE(ymaxSpy.size(),1);
// filterChildren
QSignalSpy filterChildrenSpy(drag, SIGNAL(filterChildrenChanged()));
drag->setFilterChildren(true);
QVERIFY(drag->filterChildren());
QCOMPARE(filterChildrenSpy.size(), 1);
drag->setFilterChildren(true);
QCOMPARE(filterChildrenSpy.size(), 1);
// threshold
QCOMPARE(int(drag->threshold()), qApp->styleHints()->startDragDistance());
QSignalSpy thresholdSpy(drag, SIGNAL(thresholdChanged()));
drag->setThreshold(0.0);
QCOMPARE(drag->threshold(), 0.0);
QCOMPARE(thresholdSpy.size(), 1);
drag->setThreshold(99);
QCOMPARE(thresholdSpy.size(), 2);
drag->setThreshold(99);
QCOMPARE(thresholdSpy.size(), 2);
drag->resetThreshold();
QCOMPARE(int(drag->threshold()), qApp->styleHints()->startDragDistance());
QCOMPARE(thresholdSpy.size(), 3);
}
void tst_QQuickMouseArea::resetDrag()
{
QQuickView window;
window.setInitialProperties({{"haveTarget", true}});
QVERIFY(QQuickTest::showView(window, testFileUrl("dragreset.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseRegion->drag();
QVERIFY(mouseRegion != nullptr);
QVERIFY(drag != nullptr);
// target
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QQuickItem *rootItem = qobject_cast<QQuickItem*>(window.rootObject());
QVERIFY(rootItem != nullptr);
QSignalSpy targetSpy(drag, SIGNAL(targetChanged()));
QVERIFY(drag->target() != nullptr);
auto root = window.rootObject();
QQmlProperty haveTarget {root, "haveTarget"};
haveTarget.write(false);
QCOMPARE(targetSpy.size(),1);
QVERIFY(!drag->target());
}
void tst_QQuickMouseArea::dragging()
{
QFETCH(Qt::MouseButtons, acceptedButtons);
QFETCH(Qt::MouseButton, button);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("dragging.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseRegion->drag();
QVERIFY(mouseRegion != nullptr);
QVERIFY(drag != nullptr);
mouseRegion->setAcceptedButtons(acceptedButtons);
// target
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
QPoint p = QPoint(100,100);
QTest::mousePress(&window, button, Qt::NoModifier, p);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
// First move event triggers drag, second is acted upon.
// This is due to possibility of higher stacked area taking precedence.
// The item is moved relative to the position of the mouse when the drag
// was triggered, this prevents a sudden change in position when the drag
// threshold is exceeded.
int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
// move the minimum distance to activate drag
p += QPoint(dragThreshold + 1, dragThreshold + 1);
QTest::mouseMove(&window, p);
QVERIFY(!drag->active());
// from here on move the item
p += QPoint(1, 1);
QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
// on macOS the cursor movement is going through a native event which
// means that it can actually take some time to show
QTRY_COMPARE(blackRect->x(), 50.0 + 1);
QCOMPARE(blackRect->y(), 50.0 + 1);
p += QPoint(10, 10);
QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
qreal relativeX = mouseRegion->mouseX();
qreal relativeY = mouseRegion->mouseY();
for (int i = 0; i < 20; i++) {
p += QPoint(1, 1);
QTest::mouseMove(&window, p);
QTRY_COMPARE(mouseRegion->mouseX(), relativeX);
QCOMPARE(mouseRegion->mouseY(), relativeY);
}
QVERIFY(drag->active());
QTest::mouseRelease(&window, button, Qt::NoModifier, p);
QTRY_VERIFY(!drag->active());
QTRY_COMPARE(blackRect->x(), 81.0);
QCOMPARE(blackRect->y(), 81.0);
}
void tst_QQuickMouseArea::selfDrag() // QTBUG-85111
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("selfDrag.qml")));
QQuickMouseArea *ma = window.rootObject()->findChild<QQuickMouseArea*>("ma");
QVERIFY(ma != nullptr);
QQuickDrag *drag = ma->drag();
QVERIFY(drag != nullptr);
QCOMPARE(ma, drag->target());
QQuickItem *fillRect = window.rootObject()->findChild<QQuickItem*>("fill");
QVERIFY(fillRect != nullptr);
QVERIFY(!drag->active());
QPoint p = QPoint(100,100);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
QVERIFY(!drag->active());
QCOMPARE(ma->x(), 0);
QCOMPARE(ma->y(), 0);
int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
// First move event triggers drag, second is acted upon.
// move the minimum distance to activate drag
p += QPoint(dragThreshold + 1, dragThreshold + 1);
QTest::mouseMove(&window, p);
QVERIFY(!drag->active());
// from here on move the item
p += QPoint(1, 1);
QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(ma->x(), 1);
QCOMPARE(ma->y(), 1);
p += QPoint(10, 10);
QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(ma->x(), 11);
QCOMPARE(ma->y(), 11);
qreal relativeX = ma->mouseX();
qreal relativeY = ma->mouseY();
for (int i = 0; i < 20; i++) {
p += QPoint(1, 1);
QTest::mouseMove(&window, p);
QVERIFY(drag->active());
QTRY_COMPARE(ma->mouseX(), relativeX);
QCOMPARE(ma->mouseY(), relativeY);
}
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
QTRY_VERIFY(!drag->active());
QTRY_COMPARE(ma->x(), 31);
QCOMPARE(ma->y(), 31);
}
void tst_QQuickMouseArea::dragSmoothed()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("dragging.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseRegion->drag();
drag->setThreshold(5);
mouseRegion->setAcceptedButtons(Qt::LeftButton);
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QVERIFY(!drag->active());
QTest::mouseMove(&window, QPoint(100, 102), 50);
QTest::mouseMove(&window, QPoint(100, 106), 50);
QTest::mouseMove(&window, QPoint(100, 122), 50);
QTRY_COMPARE(blackRect->x(), 50.0);
QTRY_COMPARE(blackRect->y(), 66.0);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,122));
// reset rect position
blackRect->setX(50.0);
blackRect->setY(50.0);
// now try with smoothed disabled
drag->setSmoothed(false);
QVERIFY(!drag->active());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QVERIFY(!drag->active());
QTest::mouseMove(&window, QPoint(100, 102), 50);
QTest::mouseMove(&window, QPoint(100, 106), 50);
QTest::mouseMove(&window, QPoint(100, 122), 50);
QTRY_COMPARE(blackRect->x(), 50.0);
QTRY_COMPARE(blackRect->y(), 72.0);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 122));
}
void tst_QQuickMouseArea::dragThreshold_data()
{
QTest::addColumn<bool>("preventStealing");
QTest::newRow("without preventStealing") << false;
QTest::newRow("with preventStealing") << true;
}
void tst_QQuickMouseArea::dragThreshold()
{
QFETCH(bool, preventStealing);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("dragging.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
mouseRegion->setPreventStealing(preventStealing);
QQuickDrag *drag = mouseRegion->drag();
drag->setThreshold(5);
mouseRegion->setAcceptedButtons(Qt::LeftButton);
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
QTest::mouseMove(&window, QPoint(100, 102), 50);
QVERIFY(!drag->active());
QTest::mouseMove(&window, QPoint(100, 100), 50);
QVERIFY(!drag->active());
QTest::mouseMove(&window, QPoint(100, 104), 50);
QTest::mouseMove(&window, QPoint(100, 105), 50);
QVERIFY(!drag->active());
QTest::mouseMove(&window, QPoint(100, 106), 50);
QTest::mouseMove(&window, QPoint(100, 108), 50);
QVERIFY(drag->active());
QTest::mouseMove(&window, QPoint(100, 116), 50);
QTest::mouseMove(&window, QPoint(100, 122), 50);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(blackRect->x(), 50.0);
QTRY_COMPARE(blackRect->y(), 66.0);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(122,122));
QTRY_VERIFY(!drag->active());
// Immediate drag threshold
drag->setThreshold(0);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::mouseMove(&window, QPoint(100, 122), 50);
QVERIFY(!drag->active());
QTest::mouseMove(&window, QPoint(100, 123), 50);
QVERIFY(drag->active());
QTest::mouseMove(&window, QPoint(100, 124), 50);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 124));
QTRY_VERIFY(!drag->active());
drag->resetThreshold();
}
void tst_QQuickMouseArea::invalidDrag()
{
QFETCH(Qt::MouseButtons, acceptedButtons);
QFETCH(Qt::MouseButton, button);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("dragging.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseRegion->drag();
QVERIFY(mouseRegion != nullptr);
QVERIFY(drag != nullptr);
mouseRegion->setAcceptedButtons(acceptedButtons);
// target
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
QTest::mousePress(&window, button, Qt::NoModifier, QPoint(100,100));
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
// First move event triggers drag, second is acted upon.
// This is due to possibility of higher stacked area taking precedence.
QTest::mouseMove(&window, QPoint(111,111));
QTest::qWait(50);
QTest::mouseMove(&window, QPoint(122,122));
QTest::qWait(50);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
QTest::mouseRelease(&window, button, Qt::NoModifier, QPoint(122,122));
QTest::qWait(50);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
}
void tst_QQuickMouseArea::cancelDragging()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("dragging.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseRegion->drag();
QVERIFY(mouseRegion != nullptr);
QVERIFY(drag != nullptr);
mouseRegion->setAcceptedButtons(Qt::LeftButton);
// target
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
QPoint p = QPoint(100,100);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
p += QPoint(startDragDistance() + 1, 0);
QTest::mouseMove(&window, p);
p += QPoint(11, 11);
QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
mouseRegion->QQuickItem::ungrabMouse();
QTRY_VERIFY(!drag->active());
QCOMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
QTest::mouseMove(&window, QPoint(132,132), 50);
QTRY_VERIFY(!drag->active());
QCOMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(122,122));
}
// QTBUG-58347
void tst_QQuickMouseArea::availableDistanceLessThanDragThreshold()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("availableDistanceLessThanDragThreshold.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
QVERIFY(mouseArea);
QPoint position(100, 100);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, position);
QTest::qWait(10);
position.setX(301);
QTest::mouseMove(&window, position);
position.setX(501);
QTest::mouseMove(&window, position);
QVERIFY(mouseArea->drag()->active());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, position);
QVERIFY(!mouseArea->drag()->active());
QCOMPARE(mouseArea->x(), 200.0);
}
void tst_QQuickMouseArea::setDragOnPressed()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("setDragOnPressed.qml")));
QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea *>(window.rootObject());
QVERIFY(mouseArea);
// target
QQuickItem *target = mouseArea->findChild<QQuickItem*>("target");
QVERIFY(target);
QPoint p = QPoint(100, 100);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
QQuickDrag *drag = mouseArea->drag();
QVERIFY(drag);
QVERIFY(!drag->active());
QCOMPARE(target->x(), 50.0);
QCOMPARE(target->y(), 50.0);
// First move event triggers drag, second is acted upon.
// This is due to possibility of higher stacked area taking precedence.
p += QPoint(startDragDistance() + 1, 0);
QTest::mouseMove(&window, p);
p += QPoint(11, 0);
QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(target->x(), 61.0);
QCOMPARE(target->y(), 50.0);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
QTRY_VERIFY(!drag->active());
QCOMPARE(target->x(), 61.0);
QCOMPARE(target->y(), 50.0);
}
void tst_QQuickMouseArea::updateMouseAreaPosOnClick()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("updateMousePosOnClick.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QVERIFY(mouseRegion != nullptr);
QQuickRectangle *rect = window.rootObject()->findChild<QQuickRectangle*>("ball");
QVERIFY(rect != nullptr);
QCOMPARE(mouseRegion->mouseX(), rect->x());
QCOMPARE(mouseRegion->mouseY(), rect->y());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QCOMPARE(mouseRegion->mouseX(), 100.0);
QCOMPARE(mouseRegion->mouseY(), 100.0);
QCOMPARE(mouseRegion->mouseX(), rect->x());
QCOMPARE(mouseRegion->mouseY(), rect->y());
}
void tst_QQuickMouseArea::updateMouseAreaPosOnResize()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("updateMousePosOnResize.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QVERIFY(mouseRegion != nullptr);
QQuickRectangle *rect = window.rootObject()->findChild<QQuickRectangle*>("brother");
QVERIFY(rect != nullptr);
QCOMPARE(mouseRegion->mouseX(), 0.0);
QCOMPARE(mouseRegion->mouseY(), 0.0);
QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(),
window.mapToGlobal(rect->position().toPoint()),
Qt::LeftButton, Qt::LeftButton, {});
QGuiApplication::sendEvent(&window, &event);
QVERIFY(!mouseRegion->property("emitPositionChanged").toBool());
QVERIFY(mouseRegion->property("mouseMatchesPos").toBool());
QCOMPARE(mouseRegion->property("x1").toReal(), 0.0);
QCOMPARE(mouseRegion->property("y1").toReal(), 0.0);
QCOMPARE(mouseRegion->property("x2").toReal(), rect->x());
QCOMPARE(mouseRegion->property("y2").toReal(), rect->y());
QCOMPARE(mouseRegion->mouseX(), rect->x());
QCOMPARE(mouseRegion->mouseY(), rect->y());
}
void tst_QQuickMouseArea::noOnClickedWithPressAndHold()
{
{
// We handle onPressAndHold, therefore no onClicked
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("clickandhold.qml")));
QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea*>(window.rootObject()->children().first());
QVERIFY(mouseArea);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QCOMPARE(mouseArea->pressedButtons(), Qt::LeftButton);
QVERIFY(!window.rootObject()->property("clicked").toBool());
QVERIFY(!window.rootObject()->property("held").toBool());
// timeout is 800 (in qquickmousearea.cpp)
QTest::qWait(1000);
QCoreApplication::processEvents();
QVERIFY(!window.rootObject()->property("clicked").toBool());
QVERIFY(window.rootObject()->property("held").toBool());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QTRY_VERIFY(window.rootObject()->property("held").toBool());
QVERIFY(!window.rootObject()->property("clicked").toBool());
}
{
// We do not handle onPressAndHold, therefore we get onClicked
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("noclickandhold.qml")));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QVERIFY(!window.rootObject()->property("clicked").toBool());
QTest::qWait(1000);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QVERIFY(window.rootObject()->property("clicked").toBool());
}
}
void tst_QQuickMouseArea::onMousePressRejected()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("rejectEvent.qml")));
QVERIFY(window.rootObject()->property("enabled").toBool());
QVERIFY(!window.rootObject()->property("mr1_pressed").toBool());
QVERIFY(!window.rootObject()->property("mr1_released").toBool());
QVERIFY(!window.rootObject()->property("mr1_canceled").toBool());
QVERIFY(!window.rootObject()->property("mr2_pressed").toBool());
QVERIFY(!window.rootObject()->property("mr2_released").toBool());
QVERIFY(!window.rootObject()->property("mr2_canceled").toBool());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QVERIFY(window.rootObject()->property("mr1_pressed").toBool());
QVERIFY(!window.rootObject()->property("mr1_released").toBool());
QVERIFY(!window.rootObject()->property("mr1_canceled").toBool());
QVERIFY(window.rootObject()->property("mr2_pressed").toBool());
QVERIFY(!window.rootObject()->property("mr2_released").toBool());
QVERIFY(!window.rootObject()->property("mr2_canceled").toBool());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QTRY_VERIFY(window.rootObject()->property("mr1_released").toBool());
QVERIFY(!window.rootObject()->property("mr1_canceled").toBool());
QVERIFY(!window.rootObject()->property("mr2_released").toBool());
}
void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate_data()
{
QTest::addColumn<bool>("doubleClick");
QTest::newRow("simple click") << false;
QTest::newRow("double click") << true;
}
void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate()
{
QFETCH(bool, doubleClick);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("pressedCanceled.qml")));
QVERIFY(!window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
int expectedRelease = 0;
int expectedClicks = 0;
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {});
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {});
QGuiApplication::sendEvent(&window, &pressEvent);
QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
if (doubleClick) {
QGuiApplication::sendEvent(&window, &releaseEvent);
QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks);
QGuiApplication::sendEvent(&window, &pressEvent);
QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
}
QEvent windowDeactivateEvent(QEvent::WindowDeactivate);
QGuiApplication::sendEvent(&window, &windowDeactivateEvent);
QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
QVERIFY(window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
//press again
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
//release
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks);
}
void tst_QQuickMouseArea::doubleClick()
{
QFETCH(Qt::MouseButtons, acceptedButtons);
QFETCH(Qt::MouseButton, button);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("doubleclick.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mousearea");
QVERIFY(mouseArea);
mouseArea->setAcceptedButtons(acceptedButtons);
// The sequence for a double click is:
// press, release, (click), press, double click, release
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent);
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("released").toInt(), 1);
QGuiApplication::sendEvent(&window, &pressEvent);
QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
QCOMPARE(window.rootObject()->property("released").toInt(), 2);
}
// QTBUG-14832
void tst_QQuickMouseArea::clickTwice()
{
QFETCH(Qt::MouseButtons, acceptedButtons);
QFETCH(Qt::MouseButton, button);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("clicktwice.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mousearea");
QVERIFY(mouseArea);
mouseArea->setAcceptedButtons(acceptedButtons);
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent);
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("pressed").toInt(), 1);
QCOMPARE(window.rootObject()->property("released").toInt(), 1);
QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
QGuiApplication::sendEvent(&window, &pressEvent);
QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("pressed").toInt(), 2);
QCOMPARE(window.rootObject()->property("released").toInt(), 2);
QCOMPARE(window.rootObject()->property("clicked").toInt(), 2);
}
void tst_QQuickMouseArea::invalidClick()
{
QFETCH(Qt::MouseButtons, acceptedButtons);
QFETCH(Qt::MouseButton, button);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("doubleclick.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mousearea");
QVERIFY(mouseArea);
mouseArea->setAcceptedButtons(acceptedButtons);
// The sequence for a double click is:
// press, release, (click), press, double click, release
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent);
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("released").toInt(), 0);
QGuiApplication::sendEvent(&window, &pressEvent);
QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100),
window.mapToGlobal(QPoint(100, 100)), button, button, {});
QGuiApplication::sendEvent(&window, &pressEvent2);
QGuiApplication::sendEvent(&window, &releaseEvent);
QCOMPARE(window.rootObject()->property("clicked").toInt(), 0);
QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 0);
QCOMPARE(window.rootObject()->property("released").toInt(), 0);
}
void tst_QQuickMouseArea::pressedOrdering()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("pressedOrdering.qml")));
QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("base"));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("pressed"));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("toggled"));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, {100, 100});
QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("pressed"));
}
void tst_QQuickMouseArea::preventStealing()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("preventstealing.qml")));
QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject());
QVERIFY(flickable != nullptr);
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mousearea");
QVERIFY(mouseArea != nullptr);
QSignalSpy mousePositionSpy(mouseArea, SIGNAL(positionChanged(QQuickMouseEvent*)));
QPoint p = QPoint(80, 80);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
// Without preventStealing, mouse movement over MouseArea would
// cause the Flickable to steal mouse and trigger content movement.
p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2);
QTest::mouseMove(&window, p);
p += QPoint(-10, -10);
QTest::mouseMove(&window, p);
p += QPoint(-10, -10);
QTest::mouseMove(&window, p);
p += QPoint(-10, -10);
QTest::mouseMove(&window, p);
// We should have received all four move events
QTRY_COMPARE(mousePositionSpy.size(), 4);
mousePositionSpy.clear();
QVERIFY(mouseArea->isPressed());
// Flickable content should not have moved.
QCOMPARE(flickable->contentX(), 0.);
QCOMPARE(flickable->contentY(), 0.);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
// Now allow stealing and confirm Flickable does its thing.
window.rootObject()->setProperty("stealing", false);
p = QPoint(80, 80);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
// Without preventStealing, mouse movement over MouseArea would
// cause the Flickable to steal mouse and trigger content movement.
p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2);
QTest::mouseMove(&window, p);
p += QPoint(-10, -10);
QTest::mouseMove(&window, p);
p += QPoint(-10, -10);
QTest::mouseMove(&window, p);
p += QPoint(-10, -10);
QTest::mouseMove(&window, p);
// We should only have received the first move event
QTRY_COMPARE(mousePositionSpy.size(), 1);
// Our press should be taken away
QVERIFY(!mouseArea->isPressed());
// Flickable swallows the first move, then moves 2*10 px
QTRY_COMPARE(flickable->contentX(), 20.);
QCOMPARE(flickable->contentY(), 20.);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
}
// QTBUG-103522
void tst_QQuickMouseArea::preventStealingListViewChild()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("preventStealingListViewChild.qml")));
QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject());
QVERIFY(flickable);
QQuickMouseArea *mouseArea = flickable->findChild<QQuickMouseArea*>();
QVERIFY(mouseArea);
QPoint p = mouseArea->mapToScene(mouseArea->boundingRect().center()).toPoint();
const int threshold = qApp->styleHints()->startDragDistance();
flickable->flick(0, -10000);
for (int i = 0; i < 2; ++i) {
QVERIFY(flickable->isMovingVertically());
QTest::touchEvent(&window, device).press(0, p);
QQuickTouchUtils::flush(&window);
for (int j = 0; j < 4 && !mouseArea->drag()->active(); ++j) {
p += QPoint(0, threshold);
QTest::touchEvent(&window, device).move(0, p);
QQuickTouchUtils::flush(&window);
}
// MouseArea should be dragged because of preventStealing; ListView does not steal the grab.
QVERIFY(mouseArea->drag()->active());
QCOMPARE(flickable->isDragging(), false);
QTest::touchEvent(&window, device).release(0, p);
QCOMPARE(mouseArea->drag()->active(), false);
}
}
void tst_QQuickMouseArea::clickThrough()
{
// timestamp delay to avoid generating a double click
const int doubleClickInterval = qApp->styleHints()->mouseDoubleClickInterval() + 10;
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("clickThrough.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
// With no handlers defined, click, doubleClick and PressAndHold should propagate to those with handlers
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTRY_COMPARE(root->property("presses").toInt(), 0);
QTRY_COMPARE(root->property("clicks").toInt(), 1);
QCOMPARE(root->property("doubleClicks").toInt(), 0);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::qWait(1000);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTRY_COMPARE(root->property("presses").toInt(), 0);
QTRY_COMPARE(root->property("clicks").toInt(), 1);
QTRY_COMPARE(root->property("pressAndHolds").toInt(), 1);
QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(100);
QCOMPARE(root->property("presses").toInt(), 0);
QTRY_COMPARE(root->property("clicks").toInt(), 2);
QTRY_COMPARE(root->property("doubleClicks").toInt(), 1);
QCOMPARE(root->property("pressAndHolds").toInt(), 1);
}
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("clickThrough2.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
// With handlers defined, click, doubleClick and PressAndHold should propagate only when explicitly ignored
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QCOMPARE(root->property("presses").toInt(), 0);
QCOMPARE(root->property("clicks").toInt(), 0);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::qWait(1000);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(100);
QCOMPARE(root->property("presses").toInt(), 0);
QCOMPARE(root->property("clicks").toInt(), 0);
QCOMPARE(root->property("pressAndHolds").toInt(), 0);
QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(100);
QCOMPARE(root->property("presses").toInt(), 0);
QCOMPARE(root->property("clicks").toInt(), 0);
QCOMPARE(root->property("doubleClicks").toInt(), 0);
QCOMPARE(root->property("pressAndHolds").toInt(), 0);
root->setProperty("letThrough", QVariant(true));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QCOMPARE(root->property("presses").toInt(), 0);
QTRY_COMPARE(root->property("clicks").toInt(), 1);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::qWait(1000);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(100);
QCOMPARE(root->property("presses").toInt(), 0);
QCOMPARE(root->property("clicks").toInt(), 1);
QCOMPARE(root->property("pressAndHolds").toInt(), 1);
QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(100);
QCOMPARE(root->property("presses").toInt(), 0);
QTRY_COMPARE(root->property("clicks").toInt(), 2);
QCOMPARE(root->property("doubleClicks").toInt(), 1);
QCOMPARE(root->property("pressAndHolds").toInt(), 1);
root->setProperty("noPropagation", QVariant(true));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::qWait(1000);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(100);
QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(100);
QCOMPARE(root->property("presses").toInt(), 0);
QTRY_COMPARE(root->property("clicks").toInt(), 2);
QCOMPARE(root->property("doubleClicks").toInt(), 1);
QCOMPARE(root->property("pressAndHolds").toInt(), 1);
}
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("qtbug34368.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
// QTBUG-34368 - Shouldn't propagate to disabled mouse areas
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QCOMPARE(root->property("clicksEnabled").toInt(), 1);
QCOMPARE(root->property("clicksDisabled").toInt(), 1); //Not disabled yet
root->setProperty("disableLower", QVariant(true));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100), doubleClickInterval);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QCOMPARE(root->property("clicksEnabled").toInt(), 2);
QCOMPARE(root->property("clicksDisabled").toInt(), 1); //disabled, shouldn't increment
}
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("qtbug49100.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
// QTBUG-49100
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QVERIFY(window.rootObject());
}
}
void tst_QQuickMouseArea::hoverPosition()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("hoverPosition.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QCOMPARE(root->property("mouseX").toReal(), qreal(0));
QCOMPARE(root->property("mouseY").toReal(), qreal(0));
QTest::mouseMove(&window,QPoint(10,32));
QCOMPARE(root->property("mouseX").toReal(), qreal(10));
QCOMPARE(root->property("mouseY").toReal(), qreal(32));
}
void tst_QQuickMouseArea::hoverPropagation()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("hoverPropagation.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
// QTBUG-18175, to behave like GV did.
QCOMPARE(root->property("point1").toBool(), false);
QCOMPARE(root->property("point2").toBool(), false);
QTest::mouseMove(&window, {32, 32});
QCOMPARE(root->property("point1").toBool(), true);
QCOMPARE(root->property("point2").toBool(), false);
QTest::mouseMove(&window, {232, 32});
QCOMPARE(root->property("point1").toBool(), false);
QCOMPARE(root->property("point2").toBool(), true);
}
void tst_QQuickMouseArea::hoverVisible()
{
if (QGuiApplication::platformName() == QLatin1String("minimal"))
QSKIP("Skipping due to grabWindow not functional on minimal platforms");
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("hoverVisible.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseTracker = root->findChild<QQuickMouseArea*>("mousetracker");
QVERIFY(mouseTracker != nullptr);
QSignalSpy enteredSpy(mouseTracker, SIGNAL(entered()));
// Note: We need to use a position that is different from the position in the last event
// generated in the previous test case. Otherwise it is not interpreted as a move.
QTest::mouseMove(&window,QPoint(11,33));
QCOMPARE(mouseTracker->hovered(), false);
QCOMPARE(enteredSpy.size(), 0);
mouseTracker->setVisible(true);
QCOMPARE(mouseTracker->hovered(), true);
QCOMPARE(enteredSpy.size(), 1);
QCOMPARE(QPointF(mouseTracker->mouseX(), mouseTracker->mouseY()), QPointF(11,33));
// QTBUG-77983
mouseTracker->setVisible(false);
mouseTracker->setEnabled(false);
QCOMPARE(mouseTracker->hovered(), false);
mouseTracker->setVisible(true);
// if the enabled property is false, the containsMouse property shouldn't become true
// when an invisible mousearea become visible
QCOMPARE(mouseTracker->hovered(), false);
mouseTracker->parentItem()->setEnabled(false);
mouseTracker->setVisible(false);
mouseTracker->setEnabled(true);
QCOMPARE(mouseTracker->hovered(), false);
mouseTracker->setVisible(true);
// if the parent item is not enabled, the containsMouse property will be false, even if
// the mousearea is enabled
QCOMPARE(mouseTracker->hovered(), false);
mouseTracker->parentItem()->setEnabled(true);
mouseTracker->setVisible(false);
mouseTracker->setEnabled(true);
QCOMPARE(mouseTracker->hovered(), false);
mouseTracker->setVisible(true);
QCOMPARE(mouseTracker->hovered(), true);
}
void tst_QQuickMouseArea::hoverAfterPress()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("hoverAfterPress.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
QVERIFY(mouseArea != nullptr);
QTest::mouseMove(&window, QPoint(22,33));
QCOMPARE(mouseArea->hovered(), false);
QTest::mouseMove(&window, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), true);
QTest::mouseMove(&window, QPoint(22,33));
QCOMPARE(mouseArea->hovered(), false);
QTest::mouseMove(&window, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), true);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), true);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), true);
QTest::mouseMove(&window, QPoint(22,33));
QCOMPARE(mouseArea->hovered(), false);
}
void tst_QQuickMouseArea::subtreeHoverEnabled()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("qtbug54019.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>();
QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(root);
QVERIFY(mouseArea != nullptr);
QTest::mouseMove(&window, QPoint(10, 160));
QCOMPARE(mouseArea->hovered(), false);
QVERIFY(rootPrivate->subtreeHoverEnabled);
QTest::mouseMove(&window, QPoint(10, 10));
QCOMPARE(mouseArea->hovered(), true);
QTest::mouseMove(&window, QPoint(160, 10));
QCOMPARE(mouseArea->hovered(), false);
}
void tst_QQuickMouseArea::hoverWhenDisabled()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("hoverVisible.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>();
QVERIFY(mouseArea);
mouseArea->setVisible(true);
QTest::mouseMove(&window, QPoint(50, 50));
QVERIFY(mouseArea->hovered());
mouseArea->setEnabled(false);
QTest::mouseMove(&window, QPoint(51, 50));
QVERIFY(!mouseArea->hovered());
mouseArea->setEnabled(true);
QTest::mouseMove(&window, QPoint(50, 50));
QVERIFY(mouseArea->hovered());
mouseArea->setHoverEnabled(false);
QTest::mouseMove(&window, QPoint(51, 50));
QVERIFY(!mouseArea->hovered());
mouseArea->setHoverEnabled(true);
QTest::mouseMove(&window, QPoint(50, 50));
QVERIFY(mouseArea->hovered());
}
void tst_QQuickMouseArea::disableAfterPress()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("dragging.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseArea->drag();
QVERIFY(mouseArea != nullptr);
QVERIFY(drag != nullptr);
QSignalSpy mousePositionSpy(mouseArea, SIGNAL(positionChanged(QQuickMouseEvent*)));
QSignalSpy mousePressSpy(mouseArea, SIGNAL(pressed(QQuickMouseEvent*)));
QSignalSpy mouseReleaseSpy(mouseArea, SIGNAL(released(QQuickMouseEvent*)));
// target
QQuickItem *blackRect = root->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
QPoint p = QPoint(100,100);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
QTRY_COMPARE(mousePressSpy.size(), 1);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
// First move event triggers drag, second is acted upon.
// This is due to possibility of higher stacked area taking precedence.
p += QPoint(startDragDistance() + 1, 0);
QTest::mouseMove(&window, p);
p += QPoint(11, 11);
QTest::mouseMove(&window, p);
QTRY_COMPARE(mousePositionSpy.size(), 2);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
mouseArea->setEnabled(false);
// move should still be acted upon
p += QPoint(11, 11);
QTest::mouseMove(&window, p);
p += QPoint(11, 11);
QTest::mouseMove(&window, p);
QTRY_COMPARE(mousePositionSpy.size(), 4);
QVERIFY(drag->active());
QCOMPARE(blackRect->x(), 83.0);
QCOMPARE(blackRect->y(), 83.0);
QVERIFY(mouseArea->isPressed());
QVERIFY(mouseArea->hovered());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
QTRY_COMPARE(mouseReleaseSpy.size(), 1);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 83.0);
QCOMPARE(blackRect->y(), 83.0);
QVERIFY(!mouseArea->isPressed());
QVERIFY(!mouseArea->hovered()); // since hover is not enabled
// Next press will be ignored
blackRect->setX(50);
blackRect->setY(50);
mousePressSpy.clear();
mousePositionSpy.clear();
mouseReleaseSpy.clear();
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::qWait(50);
QCOMPARE(mousePressSpy.size(), 0);
QTest::mouseMove(&window, QPoint(111,111));
QTest::qWait(50);
QTest::mouseMove(&window, QPoint(122,122));
QTest::qWait(50);
QCOMPARE(mousePositionSpy.size(), 0);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(122,122));
QTest::qWait(50);
QCOMPARE(mouseReleaseSpy.size(), 0);
}
void tst_QQuickMouseArea::disableParentOnPress_data()
{
QTest::addColumn<const QPointingDevice *>("device");
QTest::newRow("core pointer") << QPointingDevice::primaryPointingDevice();
QTest::newRow("touch") << static_cast<const QPointingDevice *>(device); // TODO QTBUG-107864
}
void tst_QQuickMouseArea::disableParentOnPress() // QTBUG-39806 and QTBUG-103788
{
QFETCH(const QPointingDevice *, device);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("disableParentOnPress.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>();
QVERIFY(mouseArea);
QSignalSpy pressedChangedSpy(mouseArea, &QQuickMouseArea::pressedChanged);
QSignalSpy canceledSpy(mouseArea, &QQuickMouseArea::canceled);
QSignalSpy enabledSpy(mouseArea, &QQuickMouseArea::enabledChanged);
QSignalSpy parentEnabledSpy(root, &QQuickItem::enabledChanged);
const QPoint p(100, 100);
QQuickTest::pointerPress(device, &window, 0, p);
QTRY_COMPARE(parentEnabledSpy.size(), 1);
QVERIFY(!root->isEnabled());
QVERIFY(mouseArea->isEnabled()); // enabled is independent, unfortunately (inverse of QTBUG-38364)
QVERIFY(!QQuickItemPrivate::get(mouseArea)->effectiveEnable);
// bug fix: it knows it got effectively disabled, so now it's no longer pressed
QVERIFY(!mouseArea->isPressed());
QCOMPARE(canceledSpy.size(), 1); // ...because the press was canceled
QCOMPARE(pressedChangedSpy.size(), 2); // kerchunk
QQuickTest::pointerRelease(device, &window, 0, p);
// now re-enable it and try again
root->setEnabled(true);
QQuickTest::pointerPress(device, &window, 0, p);
QTRY_VERIFY(!root->isEnabled());
QVERIFY(!QQuickItemPrivate::get(mouseArea)->effectiveEnable);
QVERIFY(!mouseArea->isPressed());
QCOMPARE(canceledSpy.size(), 2);
QCOMPARE(pressedChangedSpy.size(), 4);
QQuickTest::pointerRelease(device, &window, 0, p);
}
void tst_QQuickMouseArea::onWheel()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("wheel.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QWheelEvent wheelEvent(QPoint(10, 32), QPoint(10, 32), QPoint(60, 20), QPoint(0, 120),
Qt::NoButton, Qt::ControlModifier, Qt::NoScrollPhase, false);
QGuiApplication::sendEvent(&window, &wheelEvent);
QCOMPARE(root->property("angleDeltaY").toInt(), 120);
QCOMPARE(root->property("mouseX").toReal(), qreal(10));
QCOMPARE(root->property("mouseY").toReal(), qreal(32));
QCOMPARE(root->property("controlPressed").toBool(), true);
}
void tst_QQuickMouseArea::transformedMouseArea_data()
{
QTest::addColumn<bool>("insideTarget");
QTest::addColumn<QList<QPoint> >("points");
QList<QPoint> pointsInside;
pointsInside << QPoint(200, 140)
<< QPoint(140, 200)
<< QPoint(200, 200)
<< QPoint(260, 200)
<< QPoint(200, 260);
QTest::newRow("checking points inside") << true << pointsInside;
QList<QPoint> pointsOutside;
pointsOutside << QPoint(140, 140)
<< QPoint(260, 140)
<< QPoint(120, 200)
<< QPoint(280, 200)
<< QPoint(140, 260)
<< QPoint(260, 260);
QTest::newRow("checking points outside") << false << pointsOutside;
}
void tst_QQuickMouseArea::transformedMouseArea()
{
QFETCH(bool, insideTarget);
QFETCH(QList<QPoint>, points);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("transformedMouseArea.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
QVERIFY(mouseArea);
for (const QPoint &point : points) {
// check hover
QTest::mouseMove(&window, point);
QTRY_COMPARE(mouseArea->property("containsMouse").toBool(), insideTarget);
// check mouse press
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, point);
QTRY_COMPARE(mouseArea->property("pressed").toBool(), insideTarget);
// check mouse release
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, point);
QTRY_COMPARE(mouseArea->property("pressed").toBool(), false);
}
}
struct MouseEvent {
QEvent::Type type;
Qt::MouseButton button;
};
Q_DECLARE_METATYPE(MouseEvent)
void tst_QQuickMouseArea::pressedMultipleButtons_data()
{
QTest::addColumn<Qt::MouseButtons>("accepted");
QTest::addColumn<QList<MouseEvent> >("mouseEvents");
QTest::addColumn<QList<bool> >("pressed");
QTest::addColumn<QList<Qt::MouseButtons> >("pressedButtons");
QTest::addColumn<int>("changeCount");
Qt::MouseButtons accepted;
QList<MouseEvent> mouseEvents;
QList<bool> pressed;
QList<Qt::MouseButtons> pressedButtons;
int changeCount;
MouseEvent leftPress = { QEvent::MouseButtonPress, Qt::LeftButton };
MouseEvent leftRelease = { QEvent::MouseButtonRelease, Qt::LeftButton };
MouseEvent rightPress = { QEvent::MouseButtonPress, Qt::RightButton };
MouseEvent rightRelease = { QEvent::MouseButtonRelease, Qt::RightButton };
auto addRowWithFormattedTitleAndReset = [&]() {
QByteArray title;
title.append("Accept:");
if (accepted & Qt::LeftButton)
title.append(" LeftButton");
if (accepted & Qt::RightButton)
title.append(" RightButton");
title.append(" | Events:");
for (MouseEvent event : mouseEvents) {
title.append(event.type == QEvent::MouseButtonPress ? " Press" : " Release");
title.append(event.button == Qt::LeftButton ? " Left," : " Right,");
}
title.chop(1); // remove last comma
QTest::newRow(title) << accepted << mouseEvents << pressed << pressedButtons << changeCount;
mouseEvents.clear();
pressed.clear();
pressedButtons.clear();
};
accepted = Qt::LeftButton;
changeCount = 2;
mouseEvents << leftPress << rightPress << rightRelease << leftRelease;
pressed << true << true << true << false;
pressedButtons << Qt::LeftButton << Qt::LeftButton << Qt::LeftButton << Qt::NoButton;
addRowWithFormattedTitleAndReset();
accepted = Qt::LeftButton;
changeCount = 2;
mouseEvents << leftPress << rightPress << leftRelease << rightRelease;
pressed << true << true << false << false;
pressedButtons << Qt::LeftButton << Qt::LeftButton << Qt::NoButton << Qt::NoButton;
addRowWithFormattedTitleAndReset();
accepted = Qt::LeftButton | Qt::RightButton;
changeCount = 4;
mouseEvents << leftPress << rightPress << rightRelease << leftRelease;
pressed << true << true << true << false;
pressedButtons << Qt::LeftButton << (Qt::LeftButton | Qt::RightButton) << Qt::LeftButton
<< Qt::NoButton;
addRowWithFormattedTitleAndReset();
accepted = Qt::RightButton;
changeCount = 2;
mouseEvents << rightPress << leftPress << rightRelease << leftRelease;
pressed << true << true << false << false;
pressedButtons << Qt::RightButton << Qt::RightButton << Qt::NoButton << Qt::NoButton;
addRowWithFormattedTitleAndReset();
}
void tst_QQuickMouseArea::pressedMultipleButtons()
{
QFETCH(Qt::MouseButtons, accepted);
QFETCH(QList<MouseEvent>, mouseEvents);
QFETCH(QList<bool>, pressed);
QFETCH(QList<Qt::MouseButtons>, pressedButtons);
QFETCH(int, changeCount);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("simple.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mousearea");
QVERIFY(mouseArea != nullptr);
QSignalSpy pressedSpy(mouseArea, SIGNAL(pressedChanged()));
QSignalSpy pressedButtonsSpy(mouseArea, SIGNAL(pressedButtonsChanged()));
mouseArea->setAcceptedMouseButtons(accepted);
QPoint point(10, 10);
for (int i = 0; i < mouseEvents.size(); ++i) {
const MouseEvent mouseEvent = mouseEvents.at(i);
if (mouseEvent.type == QEvent::MouseButtonPress)
QTest::mousePress(&window, mouseEvent.button, Qt::NoModifier, point);
else
QTest::mouseRelease(&window, mouseEvent.button, Qt::NoModifier, point);
QCOMPARE(mouseArea->isPressed(), pressed.at(i));
QCOMPARE(mouseArea->pressedButtons(), pressedButtons.at(i));
}
QCOMPARE(pressedSpy.size(), 2);
QCOMPARE(pressedButtonsSpy.size(), changeCount);
}
void tst_QQuickMouseArea::changeAxis()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("changeAxis.qml")));
QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
QQuickDrag *drag = mouseRegion->drag();
QVERIFY(mouseRegion != nullptr);
QVERIFY(drag != nullptr);
mouseRegion->setAcceptedButtons(Qt::LeftButton);
// target
QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
QVERIFY(blackRect != nullptr);
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
// Start a diagonal drag
QPoint p = QPoint(100, 100);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
p += QPoint(startDragDistance() + 1, startDragDistance() + 1);
QTest::mouseMove(&window, p);
p += QPoint(11, 11);
QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
QCOMPARE(drag->axis(), QQuickDrag::XAndYAxis);
/* When blackRect.x becomes bigger than 75, the drag axis is changed to
* Drag.YAxis by the QML code. Verify that this happens, and that the drag
* movement is effectively constrained to the Y axis. */
p += QPoint(22, 22);
QTest::mouseMove(&window, p);
QTRY_COMPARE(blackRect->x(), 83.0);
QTRY_COMPARE(blackRect->y(), 83.0);
QTRY_COMPARE(drag->axis(), QQuickDrag::YAxis);
p += QPoint(11, 11);
QTest::mouseMove(&window, p);
QTRY_COMPARE(blackRect->y(), 94.0);
QCOMPARE(blackRect->x(), 83.0);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
QTRY_VERIFY(!drag->active());
QCOMPARE(blackRect->x(), 83.0);
QCOMPARE(blackRect->y(), 94.0);
}
#if QT_CONFIG(cursor)
void tst_QQuickMouseArea::cursorShape()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\n MouseArea {}", QUrl());
QScopedPointer<QObject> object(component.create());
QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea *>(object.data());
QVERIFY(mouseArea);
QSignalSpy spy(mouseArea, SIGNAL(cursorShapeChanged()));
QCOMPARE(mouseArea->cursorShape(), Qt::ArrowCursor);
QCOMPARE(mouseArea->cursor().shape(), Qt::ArrowCursor);
mouseArea->setCursorShape(Qt::IBeamCursor);
QCOMPARE(mouseArea->cursorShape(), Qt::IBeamCursor);
QCOMPARE(mouseArea->cursor().shape(), Qt::IBeamCursor);
QCOMPARE(spy.size(), 1);
mouseArea->setCursorShape(Qt::IBeamCursor);
QCOMPARE(spy.size(), 1);
mouseArea->setCursorShape(Qt::WaitCursor);
QCOMPARE(mouseArea->cursorShape(), Qt::WaitCursor);
QCOMPARE(mouseArea->cursor().shape(), Qt::WaitCursor);
QCOMPARE(spy.size(), 2);
}
#endif
void tst_QQuickMouseArea::moveAndReleaseWithoutPress()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("moveAndReleaseWithoutPress.qml")));
QObject *root = window.rootObject();
QVERIFY(root);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
// the press was not accepted, make sure there is no move or release event
QTest::mouseMove(&window, QPoint(110,110), 50);
// use qwait here because we want to make sure an event does NOT happen
// the test fails if the default state changes, while it shouldn't
QTest::qWait(100);
QCOMPARE(root->property("hadMove").toBool(), false);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(110,110));
QTest::qWait(100);
QCOMPARE(root->property("hadRelease").toBool(), false);
}
void tst_QQuickMouseArea::nestedStopAtBounds_data()
{
QTest::addColumn<bool>("transpose");
QTest::addColumn<bool>("invert");
QTest::newRow("left") << false << false;
QTest::newRow("right") << false << true;
QTest::newRow("top") << true << false;
QTest::newRow("bottom") << true << true;
}
void tst_QQuickMouseArea::nestedStopAtBounds()
{
QFETCH(bool, transpose);
QFETCH(bool, invert);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("nestedStopAtBounds.qml")));
QQuickMouseArea *outer = window.rootObject()->findChild<QQuickMouseArea*>("outer");
QVERIFY(outer);
QQuickMouseArea *inner = outer->findChild<QQuickMouseArea*>("inner");
QVERIFY(inner);
inner->drag()->setAxis(transpose ? QQuickDrag::YAxis : QQuickDrag::XAxis);
inner->setX(invert ? 100 : 0);
inner->setY(invert ? 100 : 0);
const int threshold = qApp->styleHints()->startDragDistance();
QPoint position(200, 200);
int &axis = transpose ? position.ry() : position.rx();
// drag toward the aligned boundary. Outer mouse area dragged.
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, position);
QTest::qWait(10);
axis += invert ? threshold * 2 : -threshold * 2;
QTest::mouseMove(&window, position);
axis += invert ? threshold : -threshold;
QTest::mouseMove(&window, position);
// outer drag will not receive mouse event, when the focus has been stolen.
// => try to regain and time out if it fails.
while (!QTest::qWaitFor([&outer]() { return outer->drag()->active(); }))
window.raise();
QCOMPARE(inner->drag()->active(), false);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, position);
QVERIFY(!outer->drag()->active());
axis = 200;
outer->setX(50);
outer->setY(50);
// drag away from the aligned boundary. Inner mouse area dragged.
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, position);
QTest::qWait(10);
axis += invert ? -threshold * 2 : threshold * 2;
QTest::mouseMove(&window, position);
axis += invert ? -threshold : threshold;
QTest::mouseMove(&window, position);
QTRY_COMPARE(outer->drag()->active(), false);
QTRY_COMPARE(inner->drag()->active(), true);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, position);
}
void tst_QQuickMouseArea::nestedFlickableStopAtBounds()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("nestedFlickableStopAtBounds.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
QVERIFY(mouseArea);
QQuickFlickable *flickable = mouseArea->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
const int threshold = qApp->styleHints()->startDragDistance();
QPoint position(200, 280);
int &pos = position.ry();
// Drag up - should move the Flickable to end
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, position);
QTest::qWait(10);
pos -= threshold * 2;
QTest::mouseMove(&window, position);
pos -= threshold * 2;
QTest::mouseMove(&window, position);
QTest::qWait(10);
pos -= 150;
QTest::mouseMove(&window, position);
QVERIFY(flickable->isDragging());
QVERIFY(!mouseArea->drag()->active());
QCOMPARE(flickable->isAtYEnd(), true);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, position);
QTRY_VERIFY(!flickable->isMoving());
pos = 280;
// Drag up again - should activate MouseArea drag
QVERIFY(!mouseArea->drag()->active());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, position);
QTest::qWait(10);
pos -= threshold * 2;
QTest::mouseMove(&window, position);
pos -= threshold * 2;
QTest::mouseMove(&window, position);
QTest::qWait(10);
pos -= 20;
QTest::mouseMove(&window, position);
QVERIFY(mouseArea->drag()->active());
QCOMPARE(flickable->isAtYEnd(), true);
QVERIFY(!flickable->isDragging());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, position);
// Drag to the top and verify that the MouseArea doesn't steal the grab when we drag back (QTBUG-56036)
pos = 50;
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, position);
QTest::qWait(10);
pos += threshold;
QTest::mouseMove(&window, position);
pos += threshold;
QTest::mouseMove(&window, position);
QTest::qWait(10);
pos += 150;
QTest::mouseMove(&window, position);
QVERIFY(flickable->isDragging());
QVERIFY(!mouseArea->drag()->active());
QCOMPARE(flickable->isAtYBeginning(), true);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, position);
QTRY_VERIFY(!flickable->isMoving());
pos = 280;
// Drag up again - should not activate MouseArea drag
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, position);
QTest::qWait(10);
pos -= threshold;
QTest::mouseMove(&window, position);
pos -= threshold;
QTest::mouseMove(&window, position);
QTest::qWait(10);
pos -= 100;
QTest::mouseMove(&window, position);
QVERIFY(flickable->isDragging());
QVERIFY(!mouseArea->drag()->active());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, position);
}
void tst_QQuickMouseArea::containsPress_data()
{
QTest::addColumn<bool>("hoverEnabled");
QTest::newRow("hover enabled") << true;
QTest::newRow("hover disaabled") << false;
}
void tst_QQuickMouseArea::containsPress()
{
QFETCH(bool, hoverEnabled);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("containsPress.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
QVERIFY(mouseArea != nullptr);
QSignalSpy containsPressSpy(mouseArea, SIGNAL(containsPressChanged()));
mouseArea->setHoverEnabled(hoverEnabled);
QTest::mouseMove(&window, QPoint(22,33));
QCOMPARE(mouseArea->hovered(), false);
QCOMPARE(mouseArea->isPressed(), false);
QCOMPARE(mouseArea->containsPress(), false);
QTest::mouseMove(&window, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), hoverEnabled);
QCOMPARE(mouseArea->isPressed(), false);
QCOMPARE(mouseArea->containsPress(), false);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), true);
QTRY_COMPARE(mouseArea->isPressed(), true);
QCOMPARE(mouseArea->containsPress(), true);
QCOMPARE(containsPressSpy.size(), 1);
QTest::mouseMove(&window, QPoint(22,33));
QCOMPARE(mouseArea->hovered(), false);
QCOMPARE(mouseArea->isPressed(), true);
QCOMPARE(mouseArea->containsPress(), false);
QCOMPARE(containsPressSpy.size(), 2);
QTest::mouseMove(&window, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), true);
QCOMPARE(mouseArea->isPressed(), true);
QCOMPARE(mouseArea->containsPress(), true);
QCOMPARE(containsPressSpy.size(), 3);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(200,200));
QCOMPARE(mouseArea->hovered(), hoverEnabled);
QCOMPARE(mouseArea->isPressed(), false);
QCOMPARE(mouseArea->containsPress(), false);
QCOMPARE(containsPressSpy.size(), 4);
}
void tst_QQuickMouseArea::ignoreBySource()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("ignoreBySource.qml")));
auto mouseDevPriv = QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice());
auto touchDevPriv = QPointingDevicePrivate::get(device);
QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject());
QVERIFY(root);
QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>("mousearea");
QVERIFY(mouseArea);
QQuickFlickable *flickable = root->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
// MouseArea should grab the press because it's interested in non-synthesized mouse events
QPoint p = QPoint(80, 80);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
QCOMPARE(mouseDevPriv->firstPointExclusiveGrabber(), mouseArea);
// That was a real mouse event
QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventNotSynthesized));
// Flickable content should not move
p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
QTest::mouseMove(&window, p);
p -= QPoint(11, 11);
QTest::mouseMove(&window, p);
p -= QPoint(11, 11);
QTest::mouseMove(&window, p);
QCOMPARE(flickable->contentX(), 0.);
QCOMPARE(flickable->contentY(), 0.);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p);
QCOMPARE(mouseDevPriv->firstPointExclusiveGrabber(), nullptr);
// Now try touch events and confirm that MouseArea ignores them, while Flickable does its thing
p = QPoint(80, 80);
QTest::touchEvent(&window, device).press(0, p, &window);
QQuickTouchUtils::flush(&window);
QCOMPARE(touchDevPriv->firstPointExclusiveGrabber(), flickable);
// That was a fake mouse event
QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventSynthesizedByQt));
p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
QTest::touchEvent(&window, device).move(0, p, &window);
p -= QPoint(11, 11);
QTest::touchEvent(&window, device).move(0, p, &window);
p -= QPoint(11, 11);
QTest::touchEvent(&window, device).move(0, p, &window);
QQuickTouchUtils::flush(&window);
QCOMPARE(touchDevPriv->firstPointExclusiveGrabber(), flickable);
QTest::touchEvent(&window, device).release(0, p, &window);
QQuickTouchUtils::flush(&window);
// Flickable content should have moved
QTRY_VERIFY(flickable->contentX() > 1);
QVERIFY(flickable->contentY() > 1);
// Now tell the MouseArea to accept only synthesized events, and repeat the tests
root->setProperty("allowedSource", Qt::MouseEventSynthesizedByQt);
flickable->setContentX(0);
flickable->setContentY(0);
// MouseArea should ignore the press because it's interested in synthesized mouse events
p = QPoint(80, 80);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
QVERIFY(mouseDevPriv->firstPointExclusiveGrabber() != mouseArea);
// That was a real mouse event
QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized);
// Flickable content should move
p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
QTest::mouseMove(&window, p);
p -= QPoint(11, 11);
QTest::mouseMove(&window, p);
p -= QPoint(11, 11);
QTest::mouseMove(&window, p);
QTRY_VERIFY(flickable->contentX() > 1);
QVERIFY(flickable->contentY() > 1);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(47, 47));
flickable->setContentX(0);
flickable->setContentY(0);
// Now try touch events and confirm that MouseArea gets them, while Flickable doesn't
p = QPoint(80, 80);
QTest::touchEvent(&window, device).press(0, p, &window);
QQuickTouchUtils::flush(&window);
QCOMPARE(touchDevPriv->firstPointExclusiveGrabber(), mouseArea);
p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
QTest::touchEvent(&window, device).move(0, p, &window);
p -= QPoint(11, 11);
QTest::touchEvent(&window, device).move(0, p, &window);
p -= QPoint(11, 11);
QTest::touchEvent(&window, device).move(0, p, &window);
QQuickTouchUtils::flush(&window);
QCOMPARE(touchDevPriv->firstPointExclusiveGrabber(), mouseArea);
QTest::touchEvent(&window, device).release(0, QPoint(47,47), &window);
QQuickTouchUtils::flush(&window);
// Flickable content should not have moved
QCOMPARE(flickable->contentX(), 0);
QCOMPARE(flickable->contentY(), 0);
}
void tst_QQuickMouseArea::notPressedAfterStolenGrab() // QTBUG-55325
{
QQuickWindow window;
window.resize(200, 200);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QQuickMouseArea *ma = new QQuickMouseArea(window.contentItem());
ma->setSize(window.size());
QObject::connect(ma,
static_cast<void (QQuickMouseArea::*)(QQuickMouseEvent*)>(&QQuickMouseArea::pressed),
[&]() { qCDebug(lcTests) << "stealing grab now"; window.contentItem()->grabMouse(); });
QTest::mouseClick(&window, Qt::LeftButton);
QVERIFY(!ma->isPressed());
}
void tst_QQuickMouseArea::pressAndHold_data()
{
QTest::addColumn<int>("pressAndHoldInterval");
QTest::addColumn<int>("waitTime");
QTest::newRow("default") << -1 << QGuiApplication::styleHints()->mousePressAndHoldInterval();
QTest::newRow("short") << 500 << 500;
QTest::newRow("long") << 1000 << 1000;
}
void tst_QQuickMouseArea::pressAndHold()
{
QFETCH(int, pressAndHoldInterval);
QFETCH(int, waitTime);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("pressAndHold.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
QVERIFY(mouseArea != nullptr);
QSignalSpy pressAndHoldSpy(mouseArea, &QQuickMouseArea::pressAndHold);
if (pressAndHoldInterval > -1)
mouseArea->setPressAndHoldInterval(pressAndHoldInterval);
else
mouseArea->resetPressAndHoldInterval();
QElapsedTimer t;
t.start();
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QVERIFY(pressAndHoldSpy.wait());
// should be off by no more than 20% of waitTime
QVERIFY(qAbs(t.elapsed() - waitTime) < (waitTime * 0.2));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
}
void tst_QQuickMouseArea::pressOneAndTapAnother_data()
{
QTest::addColumn<bool>("pressMouseFirst");
QTest::addColumn<bool>("releaseMouseFirst");
QTest::newRow("press mouse, tap touch, release mouse") << true << false; // QTBUG-64249 as written
QTest::newRow("press touch, press mouse, release touch, release mouse") << false << false;
QTest::newRow("press mouse, press touch, release mouse, release touch") << true << true;
// TODO fix in a separate patch after the 5.9->5.10 merge
// QTest::newRow("press touch, click mouse, release touch") << false << true;
}
void tst_QQuickMouseArea::pressOneAndTapAnother()
{
QFETCH(bool, pressMouseFirst);
QFETCH(bool, releaseMouseFirst);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("twoMouseAreas.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *bottomMA = root->findChild<QQuickMouseArea*>("bottom");
QVERIFY(bottomMA);
QQuickMouseArea *topMA = root->findChild<QQuickMouseArea*>("top");
QVERIFY(topMA);
QPoint upper(32, 32);
QPoint lower(32, window.height() - 32);
// press them both
if (pressMouseFirst) {
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, lower);
QTRY_COMPARE(bottomMA->isPressed(), true);
QTest::touchEvent(&window, device).press(0, lower, &window);
QQuickTouchUtils::flush(&window);
QTRY_COMPARE(bottomMA->isPressed(), true);
} else {
QTest::touchEvent(&window, device).press(0, lower, &window);
QQuickTouchUtils::flush(&window);
QTRY_COMPARE(bottomMA->isPressed(), true);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, lower);
QTRY_COMPARE(bottomMA->isPressed(), true);
}
// release them both and make sure neither one gets stuck
if (releaseMouseFirst) {
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, lower);
QTRY_COMPARE(bottomMA->isPressed(), false);
QTest::touchEvent(&window, device).release(0, upper, &window);
QQuickTouchUtils::flush(&window);
QTRY_COMPARE(topMA->isPressed(), false);
} else {
QTest::touchEvent(&window, device).release(0, upper, &window);
QQuickTouchUtils::flush(&window);
QTRY_COMPARE(topMA->isPressed(), false);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, lower);
QTRY_COMPARE(bottomMA->isPressed(), false);
}
}
void tst_QQuickMouseArea::mask()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("mask.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
// click inside the mask, and verify it registers
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100,100));
QCOMPARE(window.rootObject()->property("pressed").toInt(), 1);
QCOMPARE(window.rootObject()->property("released").toInt(), 1);
QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
// click outside the mask (but inside the MouseArea), and verify it doesn't register
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(10,10));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(10,10));
QCOMPARE(window.rootObject()->property("pressed").toInt(), 1);
QCOMPARE(window.rootObject()->property("released").toInt(), 1);
QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
}
void tst_QQuickMouseArea::nestedEventDelivery() // QTBUG-70898
{
#ifdef Q_OS_MACOS
QSKIP("this test currently crashes on MacOS 10.14 in CI. See QTBUG-86729");
#endif
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("nestedSendEvent.qml"));
QScopedPointer<QQuickWindow> window(qmlobject_cast<QQuickWindow *>(c.create()));
QVERIFY(window.data());
// Click each MouseArea and verify that it doesn't crash
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(50,150));
}
void tst_QQuickMouseArea::settingHiddenInPressUngrabs()
{
// When an item sets itself hidden, while handling pressed, it doesn't receive the grab.
// But that in turn means it doesn't see any release events, so we need to make sure it
// receives an ungrab event.
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("settingHiddenInPressUngrabs.qml"));
QScopedPointer<QQuickWindow> window(qmlobject_cast<QQuickWindow *>(c.create()));
QVERIFY(window.data());
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickMouseArea *catArea = window->findChild<QQuickMouseArea*>("cat");
QVERIFY(catArea != nullptr);
auto pointOnCatArea = catArea->mapToScene(QPointF(5.0, 5.0)).toPoint();
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, pointOnCatArea);
QCoreApplication::processEvents();
// The click hides the cat area
QTRY_VERIFY(!catArea->isVisible());
// The cat area is not stuck in pressed state.
QVERIFY(!catArea->isPressed());
QQuickMouseArea *mouseArea = window->findChild<QQuickMouseArea*>("mouse");
QVERIFY(mouseArea != nullptr);
auto pointOnMouseArea = mouseArea->mapToScene(QPointF(5.0, 5.0)).toPoint();
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, pointOnMouseArea);
QCoreApplication::processEvents();
// The click disables the mouse area
QTRY_VERIFY(!mouseArea->isEnabled());
// The mouse area is not stuck in pressed state.
QVERIFY(!mouseArea->isPressed());
}
void tst_QQuickMouseArea::negativeZStackingOrder() // QTBUG-83114
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("mouseAreasOverlapped.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *parentMouseArea = root->findChild<QQuickMouseArea*>("parentMouseArea");
QVERIFY(parentMouseArea != nullptr);
QSignalSpy clickSpyParent(parentMouseArea, &QQuickMouseArea::clicked);
QQuickMouseArea *childMouseArea = root->findChild<QQuickMouseArea*>("childMouseArea");
QVERIFY(childMouseArea != nullptr);
QSignalSpy clickSpyChild(childMouseArea, &QQuickMouseArea::clicked);
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 100));
QCOMPARE(clickSpyChild.size(), 1);
QCOMPARE(clickSpyParent.size(), 0);
auto order = root->property("clicks").toList();
QVERIFY(order.at(0) == "childMouseArea");
// Now change stacking order and try again.
childMouseArea->parentItem()->setZ(-1);
root->setProperty("clicks", QVariantList());
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 100));
QCOMPARE(clickSpyChild.size(), 1);
QCOMPARE(clickSpyParent.size(), 1);
order = root->property("clicks").toList();
QVERIFY(order.at(0) == "parentMouseArea");
}
// QTBUG-87197
void tst_QQuickMouseArea::containsMouseAndVisibility()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("containsMouse.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
QVERIFY(mouseArea != nullptr);
QVERIFY(!mouseArea->isVisible());
QTest::mouseMove(&window, QPoint(10, 10));
QTRY_VERIFY(!mouseArea->hovered());
mouseArea->setVisible(true);
QVERIFY(mouseArea->isVisible());
QTRY_VERIFY(mouseArea->hovered());
/* we (ab-)use QPointF() as the 'reset' value in QQuickWindow's leave-event handling,
but can't verify that this leaves an invalid interpretation of states for position
QPoint(0, 0) as QTest::mouseMove interprets a null-position as "center of the window".
So instead, verify the current (unexpectedly expected) behavior as far as testing is
concern.
*/
QTest::mouseMove(&window, QPoint(0, 0));
QTRY_VERIFY(mouseArea->hovered());
QTRY_VERIFY(mouseArea->isUnderMouse());
// move to the edge (can't move outside)
QTest::mouseMove(&window, QPoint(window.width() - 1, window.height() / 2));
// then pretend we left
QEvent event(QEvent::Leave);
QGuiApplication::sendEvent(&window, &event);
QVERIFY(!mouseArea->hovered());
// toggle mouse area visibility - the hover state should not change
mouseArea->setVisible(false);
QVERIFY(!mouseArea->isVisible());
QVERIFY(!mouseArea->hovered());
mouseArea->setVisible(true);
QVERIFY(mouseArea->isVisible());
QVERIFY(!mouseArea->hovered());
}
// QTBUG-109567
void tst_QQuickMouseArea::containsMouseAndVisibilityMasked()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("containsMouseMasked.qml")));
QQuickMouseArea *mouseArea1 = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea1");
QVERIFY(mouseArea1 != nullptr);
QVERIFY(mouseArea1->isVisible());
QQuickMouseArea *mouseArea2 = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea2");
QVERIFY(mouseArea2 != nullptr);
QVERIFY(mouseArea2->isVisible());
QTest::mouseMove(&window, QPoint(window.width() / 2, window.height() / 2));
// Check that mouseArea" (i.e. the masking MouseArea) is the only hovered MouseArea.
QTRY_VERIFY(!mouseArea1->hovered());
QTRY_VERIFY(mouseArea2->hovered());
// Toggle the visibility of the masked MouseArea (mouseArea1).
mouseArea1->setVisible(false);
QVERIFY(!mouseArea1->isVisible());
mouseArea1->setVisible(true);
QVERIFY(mouseArea1->isVisible());
// Check that the masked MouseArea is not now hovered depite being under the mouse after
// changing the visibility to visible. mouseArea2 should be the only hovered MouseArea still.
QTRY_VERIFY(!mouseArea1->hovered());
QTRY_VERIFY(mouseArea2->hovered());
QTest::mouseMove(&window, QPoint(10, 10));
QTRY_VERIFY(mouseArea1->hovered());
QTRY_VERIFY(!mouseArea2->hovered());
// Toggle the visibility of the masked MouseArea (mouseArea1).
mouseArea1->setVisible(false);
QVERIFY(!mouseArea1->isVisible());
mouseArea1->setVisible(true);
QVERIFY(mouseArea1->isVisible());
QTRY_VERIFY(mouseArea1->hovered());
QTRY_VERIFY(!mouseArea2->hovered());
}
// QTBUG-110594
void tst_QQuickMouseArea::containsMouseAndHoverDisabled()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("containsMouseAndHoverDisabled.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
QVERIFY(mouseArea != nullptr);
QVERIFY(!mouseArea->hoverEnabled());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
QTRY_VERIFY(!mouseArea->hovered());
}
// QTBUG-35995 and QTBUG-102158
void tst_QQuickMouseArea::doubleClickToHide()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("doubleClickToHide.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>();
QVERIFY(mouseArea);
QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
QCOMPARE(mouseArea->isVisible(), false);
QCOMPARE(mouseArea->isPressed(), false);
QCOMPARE(mouseArea->pressedButtons(), Qt::NoButton);
mouseArea->setVisible(true);
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
QCOMPARE(window.rootObject()->property("clicked").toInt(), 2);
}
void tst_QQuickMouseArea::releaseFirstTouchAfterSecond() // QTBUG-103766
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("simple.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>();
QVERIFY(mouseArea);
QSignalSpy pressSpy(mouseArea, SIGNAL(pressed(QQuickMouseEvent*)));
QSignalSpy releaseSpy(mouseArea, &QQuickMouseArea::released);
QTest::touchEvent(&window, device).press(0, {20, 20});
QTRY_COMPARE(pressSpy.size(), 1);
QTest::touchEvent(&window, device).stationary(0).press(1, {100, 20});
QCOMPARE(pressSpy.size(), 1); // touchpoint 0 is the touchmouse, touchpoint 1 is ignored
QTest::touchEvent(&window, device).stationary(0).release(1, {100, 20});
QCOMPARE(releaseSpy.size(), 0); // touchpoint 0 is the touchmouse, and remains pressed
QTest::touchEvent(&window, device).release(0, {20, 20});
QTRY_COMPARE(releaseSpy.size(), 1);
}
#if QT_CONFIG(tabletevent)
void tst_QQuickMouseArea::tabletStylusTap()
{
QVERIFY(qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents)); // MouseArea depends on it
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("simple.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>();
QVERIFY(mouseArea);
QSignalSpy pressSpy(mouseArea, SIGNAL(pressed(QQuickMouseEvent*)));
QSignalSpy releaseSpy(mouseArea, &QQuickMouseArea::released);
QSignalSpy clickSpy(mouseArea, &QQuickMouseArea::clicked);
const qint64 stylusId = 1234567890;
const QPoint point(100,100);
QWindowSystemInterface::handleTabletEvent(&window, point, window.mapToGlobal(point),
int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
Qt::LeftButton, 0.5, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
if (QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse)
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, point); // simulate what the platform does
QTRY_COMPARE(pressSpy.size(), 1);
QWindowSystemInterface::handleTabletEvent(&window, point, window.mapToGlobal(point),
int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
Qt::NoButton, 0.5, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
if (QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse)
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, point);
QTRY_COMPARE(releaseSpy.size(), 1);
QCOMPARE(clickSpy.size(), 1);
QCOMPARE(pressSpy.size(), 1);
}
#endif
void tst_QQuickMouseArea::syntheticRightClick()
{
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("simple.qml")));
QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>();
QVERIFY(mouseArea);
mouseArea->setAcceptedButtons(Qt::RightButton);
QSignalSpy clickSpy(mouseArea, &QQuickMouseArea::clicked);
const QPointF p(20, 20);
quint64 timestamp = 10;
// The right-click is probably synthesized from a touch long-press IRL, but it doesn't matter for the DA's logic.
// We could set QT_QUICK_ALLOW_SYNTHETIC_RIGHT_CLICK=0 to opt out, but otherwise it's allowed.
QMouseEvent press(QEvent::MouseButtonPress, p, mouseArea->mapToScene(p), mouseArea->mapToGlobal(p),
Qt::RightButton, Qt::RightButton, Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
press.setTimestamp(timestamp++);
QGuiApplication::sendEvent(&window, &press);
QCOMPARE(mouseArea->pressedButtons(), Qt::RightButton);
QMouseEvent release(QEvent::MouseButtonRelease, p, mouseArea->mapToScene(p), mouseArea->mapToGlobal(p),
Qt::RightButton, Qt::RightButton, Qt::NoModifier, Qt::MouseEventSynthesizedBySystem);
release.setTimestamp(timestamp);
QGuiApplication::sendEvent(&window, &release);
QCOMPARE(mouseArea->pressedButtons(), Qt::NoButton);
QCOMPARE(clickSpy.size(), 1);
}
QTEST_MAIN(tst_QQuickMouseArea)
#include "tst_qquickmousearea.moc"