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

2630 lines
96 KiB
C++

// 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>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <private/qquickflickable_p.h>
#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"