diff options
-rw-r--r-- | src/qml/doc/src/qmltypereference.qdoc | 2 | ||||
-rw-r--r-- | src/quick/doc/src/qmltypereference.qdoc | 6 | ||||
-rw-r--r-- | src/quick/items/qquickevents.cpp | 50 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 6 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 2 | ||||
-rw-r--r-- | src/quick/items/qquickmousearea.cpp | 10 | ||||
-rw-r--r-- | src/quick/items/qquickmousearea_p.h | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/data/ignoreBySource.qml | 31 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/qquickmousearea.pro | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 114 |
10 files changed, 215 insertions, 9 deletions
diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index 31133c862f..f32574fcc1 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -55,7 +55,7 @@ are also provided by the \c QtQuick namespace which may be imported as follows: \qml -import QtQuick 2.5 +import QtQuick 2.7 \endqml See the \l{Qt Quick} module documentation for more information about the \c diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 630e0f58bc..6e6e66e026 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! -\qmlmodule QtQuick 2.5 +\qmlmodule QtQuick 2.7 \title Qt Quick QML Types \ingroup qmlmodules \brief Provides graphical QML types. @@ -34,11 +34,11 @@ The \l{Qt Quick} module provides graphical primitive types. These types are only available in a QML document if that document imports the \c QtQuick namespace. -The current version of the \c QtQuick module is version 2.5, and thus it may be +The current version of the \c QtQuick module is version 2.7, and thus it may be imported via the following statement: \qml -import QtQuick 2.5 +import QtQuick 2.7 \endqml Visit the \l {Qt Quick} module documentation for more diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 7706412a5f..5061c19f1e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -263,6 +263,56 @@ Item { \endqml */ +/*! + \qmlproperty int QtQuick::MouseEvent::source + \since 5.7 + + This property holds the source of the mouse event. + + The mouse event source can be used to distinguish between genuine and + artificial mouse events. When using other pointing devices such as + touchscreens and graphics tablets, if the application does not make use of + the actual touch or tablet events, mouse events may be synthesized by the + operating system or by Qt itself. + + The value can be one of: + + \value Qt.MouseEventNotSynthesized The most common value. On platforms where + such information is available, this value indicates that the event + represents a genuine mouse event from the system. + + \value Qt.MouseEventSynthesizedBySystem Indicates that the mouse event was + synthesized from a touch or tablet event by the platform. + + \value Qt.MouseEventSynthesizedByQt Indicates that the mouse event was + synthesized from an unhandled touch or tablet event by Qt. + + \value Qt.MouseEventSynthesizedByApplication Indicates that the mouse event + was synthesized by the application. This allows distinguishing + application-generated mouse events from the ones that are coming from the + system or are synthesized by Qt. + + For example, to react only to events which come from an actual mouse: + \qml + MouseArea { + onPressed: if (mouse.source !== Qt.MouseEventNotSynthesized) { + mouse.accepted = false + } + + onClicked: doSomething() + } + \endqml + + If the handler for the press event rejects the event, it will be propagated + further, and then another Item underneath can handle synthesized events + from touchscreens. For example, if a Flickable is used underneath (and the + MouseArea is not a child of the Flickable), it can be useful for the + MouseArea to handle genuine mouse events in one way, while allowing touch + events to fall through to the Flickable underneath, so that the ability to + flick on a touchscreen is retained. In that case the ability to drag the + Flickable via mouse would be lost, but it does not prevent Flickable from + receiving mouse wheel events. +*/ /*! \qmltype WheelEvent diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index f0d7769705..b28ab555b0 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -103,6 +103,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject Q_PROPERTY(int button READ button) Q_PROPERTY(int buttons READ buttons) Q_PROPERTY(int modifiers READ modifiers) + Q_PROPERTY(int source READ source REVISION 7) Q_PROPERTY(bool wasHeld READ wasHeld) Q_PROPERTY(bool isClick READ isClick) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) @@ -111,13 +112,14 @@ public: QQuickMouseEvent(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers , bool isClick=false, bool wasHeld=false) : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers) - , _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {} + , _source(Qt::MouseEventNotSynthesized), _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {} qreal x() const { return _x; } qreal y() const { return _y; } int button() const { return _button; } int buttons() const { return _buttons; } int modifiers() const { return _modifiers; } + int source() const { return _source; } bool wasHeld() const { return _wasHeld; } bool isClick() const { return _isClick; } @@ -125,6 +127,7 @@ public: void setX(qreal x) { _x = x; } void setY(qreal y) { _y = y; } void setPosition(const QPointF &point) { _x = point.x(); _y = point.y(); } + void setSource(Qt::MouseEventSource s) { _source = s; } bool isAccepted() { return _accepted; } void setAccepted(bool accepted) { _accepted = accepted; } @@ -135,6 +138,7 @@ private: Qt::MouseButton _button; Qt::MouseButtons _buttons; Qt::KeyboardModifiers _modifiers; + Qt::MouseEventSource _source; bool _wasHeld; bool _isClick; bool _accepted; diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index ce4f81e1dd..23245e4a7b 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -285,6 +285,8 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickGridView, 7>(uri, 2, 7, "GridView"); qmlRegisterType<QQuickTextInput, 7>(uri, 2, 7, "TextInput"); qmlRegisterType<QQuickTextEdit, 7>(uri, 2, 7, "TextEdit"); + + qmlRegisterUncreatableType<QQuickMouseEvent, 7>(uri, 2, 7, nullptr, QQuickMouseEvent::tr("MouseEvent is only available within handlers in MouseArea")); } static void initResources() diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 42de98eff7..920a86881b 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -685,7 +685,7 @@ void QQuickMouseArea::mousePressEvent(QMouseEvent *event) d->startScene = event->windowPos(); d->pressAndHoldTimer.start(QGuiApplication::styleHints()->mousePressAndHoldInterval(), this); setKeepMouseGrab(d->stealMouse); - event->setAccepted(setPressed(event->button(), true)); + event->setAccepted(setPressed(event->button(), true, event->source())); } } @@ -762,6 +762,7 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) #endif QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + me.setSource(event->source()); emit mouseXChanged(&me); me.setPosition(d->lastPos); emit mouseYChanged(&me); @@ -777,7 +778,7 @@ void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) QQuickItem::mouseReleaseEvent(event); } else { d->saveEvent(event); - setPressed(event->button(), false); + setPressed(event->button(), false, event->source()); if (!d->pressed) { // no other buttons are pressed #ifndef QT_NO_DRAGANDDROP @@ -802,6 +803,7 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event) if (d->enabled) { d->saveEvent(event); QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); + me.setSource(event->source()); me.setAccepted(d->isDoubleClickConnected()); emit this->doubleClicked(&me); if (!me.isAccepted()) @@ -996,6 +998,7 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event) if (d->pressed && dragged == false && d->hovered == true) { d->longPress = true; QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + me.setSource(Qt::MouseEventSynthesizedByQt); me.setAccepted(d->isPressAndHoldConnected()); emit pressAndHold(&me); if (!me.isAccepted()) @@ -1158,7 +1161,7 @@ void QQuickMouseArea::setAcceptedButtons(Qt::MouseButtons buttons) } } -bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p) +bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventSource source) { Q_D(QQuickMouseArea); @@ -1173,6 +1176,7 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p) if (wasPressed != p) { QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress); + me.setSource(source); if (p) { d->pressed |= button; if (!d->doubleClick) diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h index ae3e3b1e5a..5cd86541d4 100644 --- a/src/quick/items/qquickmousearea_p.h +++ b/src/quick/items/qquickmousearea_p.h @@ -155,7 +155,7 @@ Q_SIGNALS: protected: void setHovered(bool); - bool setPressed(Qt::MouseButton button, bool); + bool setPressed(Qt::MouseButton button, bool p, Qt::MouseEventSource source); bool sendMouseEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; diff --git a/tests/auto/quick/qquickmousearea/data/ignoreBySource.qml b/tests/auto/quick/qquickmousearea/data/ignoreBySource.qml new file mode 100644 index 0000000000..a53cbf7b1d --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/ignoreBySource.qml @@ -0,0 +1,31 @@ +import QtQuick 2.7 + +Item { + id: root + // by default you can flick via touch or tablet but not via mouse + property int allowedSource: Qt.MouseEventNotSynthesized + property int lastEventSource: -1 + width: 200 + height: 200 + Flickable { + objectName: "flickable" + anchors.fill: parent + contentWidth: 400 + contentHeight: 400 + Rectangle { + color: ma.pressed ? "yellow" : "steelblue" + width: 200 + height: 200 + } + } + MouseArea { + id: ma + objectName: "mousearea" + onPressed: { + root.lastEventSource = mouse.source + if (mouse.source !== root.allowedSource) + mouse.accepted = false + } + anchors.fill: parent + } +} diff --git a/tests/auto/quick/qquickmousearea/qquickmousearea.pro b/tests/auto/quick/qquickmousearea/qquickmousearea.pro index 15a080aa3e..3a4dfa946f 100644 --- a/tests/auto/quick/qquickmousearea/qquickmousearea.pro +++ b/tests/auto/quick/qquickmousearea/qquickmousearea.pro @@ -7,6 +7,7 @@ SOURCES += tst_qquickmousearea.cpp \ ../../shared/testhttpserver.cpp include (../../shared/util.pri) +include (../shared/util.pri) TESTDATA = data/* diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index d2994ca0b4..3f78bcf1eb 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -36,9 +36,11 @@ #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlengine.h> #include "../../shared/util.h" +#include "../shared/viewtestutil.h" #include <QtGui/qstylehints.h> #include <QtGui/QCursor> #include <QtGui/QScreen> +#include <qpa/qwindowsysteminterface.h> // Initialize view, set Url, center in available geometry, move mouse away if desired static bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut, QByteArray *errorMessage) @@ -68,7 +70,13 @@ static bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut, QByteArr class tst_QQuickMouseArea: public QQmlDataTest { Q_OBJECT +public: + tst_QQuickMouseArea() + : device(nullptr) + {} + private slots: + void initTestCase() Q_DECL_OVERRIDE; void dragProperties(); void resetDrag(); void dragging_data() { acceptedButton_data(); } @@ -113,15 +121,27 @@ private slots: void nestedStopAtBounds_data(); void containsPress_data(); void containsPress(); + void ignoreBySource(); private: void acceptedButton_data(); void rejectedButton_data(); + QTouchDevice *device; }; Q_DECLARE_METATYPE(Qt::MouseButton) Q_DECLARE_METATYPE(Qt::MouseButtons) +void tst_QQuickMouseArea::initTestCase() +{ + QQmlDataTest::initTestCase(); + if (!device) { + device = new QTouchDevice; + device->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(device); + } +} + void tst_QQuickMouseArea::acceptedButton_data() { QTest::addColumn<Qt::MouseButtons>("acceptedButtons"); @@ -1790,6 +1810,100 @@ void tst_QQuickMouseArea::containsPress() QCOMPARE(containsPressSpy.count(), 4); } +void tst_QQuickMouseArea::ignoreBySource() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(initView(window, testFileUrl("ignoreBySource.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QVERIFY(window.rootObject()); + + 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 + QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80)); + QVERIFY(window.mouseGrabberItem() == mouseArea); + // That was a real mouse event + QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized); + + // Flickable content should not move + QTest::mouseMove(&window,QPoint(69,69)); + QTest::mouseMove(&window,QPoint(58,58)); + QTest::mouseMove(&window,QPoint(47,47)); + QCOMPARE(flickable->contentX(), 0.); + QCOMPARE(flickable->contentY(), 0.); + + QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(47, 47)); + + // Now try touch events and confirm that MouseArea ignores them, while Flickable does its thing + + QTest::touchEvent(&window, device).press(0, QPoint(80, 80), &window); + QQuickTouchUtils::flush(&window); + QVERIFY(window.mouseGrabberItem() != mouseArea); + // That was a fake mouse event + QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventSynthesizedByQt)); + QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); + QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); + QTest::touchEvent(&window, device).move(0, QPoint(47,47), &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(window.mouseGrabberItem(), flickable); + QTest::touchEvent(&window, device).release(0, QPoint(47,47), &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 + QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80)); + QVERIFY(window.mouseGrabberItem() != mouseArea); + // That was a real mouse event + QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized); + + // Flickable content should move + QTest::mouseMove(&window,QPoint(69,69)); + QTest::mouseMove(&window,QPoint(58,58)); + QTest::mouseMove(&window,QPoint(47,47)); + QTRY_VERIFY(flickable->contentX() > 1); + QVERIFY(flickable->contentY() > 1); + + QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(47, 47)); + flickable->setContentX(0); + flickable->setContentY(0); + + // Now try touch events and confirm that MouseArea gets them, while Flickable doesn't + + QTest::touchEvent(&window, device).press(0, QPoint(80, 80), &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(window.mouseGrabberItem(), mouseArea); + QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); + QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window); + QTest::touchEvent(&window, device).move(0, QPoint(47,47), &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(window.mouseGrabberItem(), 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.); +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" |