aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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"