aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@theqtcompany.com>2016-03-04 11:02:54 +0100
committerShawn Rutledge <shawn.rutledge@theqtcompany.com>2016-03-11 12:15:26 +0000
commit644a6a42a45ceb9790df6d6ee1c3ba43c7b9ab10 (patch)
tree493c315698238fb1736cbd29d90a52d5351feb0a
parent6300f4dde0ab3279b18a0fb29f94cfe142557cba (diff)
MouseArea: add source property to mouse event
It comes from the source() of the QMouseEvent which triggered it. This makes it possible to distinguish real mouse events from those that are synthesized from touch or tablet. And for this we need to import QtQuick 2.7 [ChangeLog][QtQuick][MouseArea] Added mouse.source property to enable distinguishing genuine mouse events from those that are synthesized from touch or tablet events. Change-Id: I568964f63981703bd23e05daac5288518f09d837 Reviewed-by: Robin Burchell <robin.burchell@viroteck.net>
-rw-r--r--src/qml/doc/src/qmltypereference.qdoc2
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc6
-rw-r--r--src/quick/items/qquickevents.cpp50
-rw-r--r--src/quick/items/qquickevents_p_p.h6
-rw-r--r--src/quick/items/qquickitemsmodule.cpp2
-rw-r--r--src/quick/items/qquickmousearea.cpp10
-rw-r--r--src/quick/items/qquickmousearea_p.h2
-rw-r--r--tests/auto/quick/qquickmousearea/data/ignoreBySource.qml31
-rw-r--r--tests/auto/quick/qquickmousearea/qquickmousearea.pro1
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp114
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"