MultiPointTouchArea: handles mouse too, unless mouseEnabled is false
authorShawn Rutledge <shawn.rutledge@digia.com>
Wed, 8 May 2013 13:21:59 +0000 (15:21 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 10 Mar 2014 14:01:14 +0000 (15:01 +0100)
A new boolean property mouseEnabled is introduced (true by default).
If set to true, then it will handle any non-synthetic mouse event
as if it were a touch point.
If set to false, the area becomes transparent for real mouse events
so that a MultiPointTouchArea can be stacked on top of a MouseArea
in order to separate handling of touch and mouse.
In either case it continues to absorb and ignore synthesized mouse
events (including touch-to-mouse synthesis in QQuickWindow).

[ChangeLog][QtQuick][MultiPointTouchArea]handles mouse as a touchpoint;
added mouseEnabled property to permit transparent pass-through to
mouse-sensitive items

Change-Id: I4af94d838f0060154494589c0f15c6858ee89ddb
Task-number: QTBUG-31047
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
src/quick/items/qquickmultipointtoucharea.cpp
src/quick/items/qquickmultipointtoucharea_p.h
tests/auto/quick/qquickmultipointtoucharea/data/dualGestures.qml [new file with mode: 0644]
tests/auto/quick/qquickmultipointtoucharea/data/inMouseArea.qml [new file with mode: 0644]
tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml [new file with mode: 0644]
tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp

index 973f6ef..6a5ba93 100644 (file)
@@ -236,7 +236,14 @@ void QQuickTouchPoint::setSceneY(qreal sceneY)
     A MultiPointTouchArea is an invisible item that is used to track multiple touch points.
 
     The \l Item::enabled property is used to enable and disable touch handling. When disabled,
-    the touch area becomes transparent to mouse/touch events.
+    the touch area becomes transparent to mouse and touch events.
+
+    By default, the mouse will be handled the same way as a single touch point,
+    and items under the touch area will not receive mouse events because the
+    touch area is handling them. But if the \l mouseEnabled property is set to
+    false, it becomes transparent to mouse events so that another
+    mouse-sensitive Item (such as a MouseArea) can be used to handle mouse
+    interaction separately.
 
     MultiPointTouchArea can be used in two ways:
 
@@ -313,6 +320,10 @@ void QQuickTouchPoint::setSceneY(qreal sceneY)
 
     This property holds a set of user-defined touch point objects that can be bound to.
 
+    If mouseEnabled is true (the default) and the left mouse button is pressed
+    while the mouse is over the touch area, the current mouse position will be
+    one of these touch points.
+
     In the following example, we have two small rectangles that follow our touch points.
 
     \snippet qml/multipointtoucharea/multipointtoucharea.qml 0
@@ -326,7 +337,9 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
     : QQuickItem(parent),
       _minimumTouchPoints(0),
       _maximumTouchPoints(INT_MAX),
-      _stealMouse(false)
+      _mouseTouchPoint(Q_NULLPTR),
+      _stealMouse(false),
+      _mouseEnabled(true)
 {
     setAcceptedMouseButtons(Qt::LeftButton);
     setFiltersChildMouseEvents(true);
@@ -358,6 +371,11 @@ QQuickMultiPointTouchArea::~QQuickMultiPointTouchArea()
     one handling two finger touches, and another handling three finger touches.
 
     By default, all touch points within the touch area are handled.
+
+    If mouseEnabled is true, the mouse acts as a touch point, so it is also
+    subject to these constraints: for example if maximumTouchPoints is two, you
+    can use the mouse as one touch point and a finger as another touch point
+    for a total of two.
 */
 
 int QQuickMultiPointTouchArea::minimumTouchPoints() const
@@ -386,6 +404,25 @@ void QQuickMultiPointTouchArea::setMaximumTouchPoints(int num)
     emit maximumTouchPointsChanged();
 }
 
+/*!
+    \qmlproperty bool QtQuick::MultiPointTouchArea::mouseEnabled
+
+    This property controls whether the MultiPointTouchArea will handle mouse
+    events too. If it is true (the default), the touch area will treat the
+    mouse the same as a single touch point; if it is false, the touch area will
+    ignore mouse events and allow them to "pass through" so that they can be
+    handled by other items underneath.
+*/
+void QQuickMultiPointTouchArea::setMouseEnabled(bool arg)
+{
+    if (_mouseEnabled != arg) {
+        _mouseEnabled = arg;
+        if (_mouseTouchPoint && !arg)
+            _mouseTouchPoint = Q_NULLPTR;
+        emit mouseEnabledChanged();
+    }
+}
+
 void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event)
 {
     switch (event->type()) {
@@ -434,10 +471,46 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
     bool ended = false;
     bool moved = false;
     bool started = false;
+    bool isMouseEvent = false;
 
     clearTouchLists();
-    QTouchEvent *e = static_cast<QTouchEvent*>(event);
-    QList<QTouchEvent::TouchPoint> touchPoints = e->touchPoints();
+    QList<QTouchEvent::TouchPoint> touchPoints;
+
+    switch (event->type()) {
+    case QEvent::TouchBegin:
+    case QEvent::TouchUpdate:
+    case QEvent::TouchEnd:
+        touchPoints = static_cast<QTouchEvent*>(event)->touchPoints();
+        break;
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease: {
+        QMouseEvent *me = static_cast<QMouseEvent*>(event);
+        _mouseQpaTouchPoint.setPos(me->localPos());
+        _mouseQpaTouchPoint.setScenePos(me->windowPos());
+        _mouseQpaTouchPoint.setScreenPos(me->screenPos());
+        if (event->type() == QEvent::MouseMove)
+            _mouseQpaTouchPoint.setState(Qt::TouchPointMoved);
+        else if (event->type() == QEvent::MouseButtonRelease)
+            _mouseQpaTouchPoint.setState(Qt::TouchPointReleased);
+        else { // QEvent::MouseButtonPress
+            _mouseQpaTouchPoint.setState(Qt::TouchPointPressed);
+            _pressedTouchPoints.append(_mouseTouchPoint);
+        }
+        touchPoints << _mouseQpaTouchPoint;
+        isMouseEvent = true;
+        break;
+    }
+    default:
+        qWarning("updateTouchData: unhandled event type %d", event->type());
+        break;
+    }
+
+    if (!isMouseEvent && _mouseTouchPoint) {
+        QQuickWindow *c = window();
+        if (c && c->mouseGrabberItem() == this)
+            touchPoints << _mouseQpaTouchPoint;
+    }
     int numTouchPoints = touchPoints.count();
     //always remove released touches, and make sure we handle all releases before adds.
     foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
@@ -517,10 +590,12 @@ void QQuickMultiPointTouchArea::clearTouchLists()
 {
     foreach (QObject *obj, _releasedTouchPoints) {
         QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
-        if (!dtp->isQmlDefined())
+        if (!dtp->isQmlDefined()) {
+            _touchPoints.remove(dtp->pointId());
             delete dtp;
-        else
+        } else {
             dtp->setInUse(false);
+        }
     }
     _releasedTouchPoints.clear();
     _pressedTouchPoints.clear();
@@ -547,6 +622,25 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
     _pressedTouchPoints.append(dtp);
 }
 
+void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e)
+{
+    QQuickTouchPoint *dtp = 0;
+    foreach (QQuickTouchPoint *tp, _touchPrototypes)
+        if (!tp->inUse()) {
+            tp->setInUse(true);
+            dtp = tp;
+            break;
+        }
+
+    if (dtp == 0)
+        dtp = new QQuickTouchPoint(false);
+    updateTouchPoint(dtp, e);
+    dtp->setPressed(true);
+    _touchPoints.insert(-1, dtp);
+    _pressedTouchPoints.append(dtp);
+    _mouseTouchPoint = dtp;
+}
+
 #ifdef Q_OS_OSX
 void QQuickMultiPointTouchArea::hoverEnterEvent(QHoverEvent *event)
 {
@@ -597,9 +691,23 @@ void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QT
     dtp->setSceneY(p->scenePos().y());
 }
 
+void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QMouseEvent *e)
+{
+    dtp->setPreviousX(dtp->x());
+    dtp->setPreviousY(dtp->y());
+    dtp->setX(e->localPos().x());
+    dtp->setY(e->localPos().y());
+    if (e->type() == QEvent::MouseButtonPress) {
+        dtp->setStartX(e->localPos().x());
+        dtp->setStartY(e->localPos().y());
+    }
+    dtp->setSceneX(e->windowPos().x());
+    dtp->setSceneY(e->windowPos().y());
+}
+
 void QQuickMultiPointTouchArea::mousePressEvent(QMouseEvent *event)
 {
-    if (!isEnabled()) {
+    if (!isEnabled() || !_mouseEnabled || event->button() != Qt::LeftButton) {
         QQuickItem::mousePressEvent(event);
         return;
     }
@@ -607,25 +715,53 @@ void QQuickMultiPointTouchArea::mousePressEvent(QMouseEvent *event)
     _stealMouse = false;
     setKeepMouseGrab(false);
     event->setAccepted(true);
+    _mousePos = event->localPos();
+
+    if (event->source() != Qt::MouseEventNotSynthesized)
+        return;
+
+    if (_touchPoints.count() >= _minimumTouchPoints - 1 && _touchPoints.count() < _maximumTouchPoints) {
+        addTouchPoint(event);
+        updateTouchData(event);
+        emit pressed(_pressedTouchPoints);
+    }
 }
 
 void QQuickMultiPointTouchArea::mouseMoveEvent(QMouseEvent *event)
 {
-    if (!isEnabled()) {
+    if (!isEnabled() || !_mouseEnabled) {
         QQuickItem::mouseMoveEvent(event);
         return;
     }
 
-    //do nothing
+    if (event->source() != Qt::MouseEventNotSynthesized)
+        return;
+
+    _movedTouchPoints.clear();
+    updateTouchData(event);
 }
 
 void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event)
 {
     _stealMouse = false;
-    if (!isEnabled()) {
+    if (!isEnabled() || !_mouseEnabled) {
         QQuickItem::mouseReleaseEvent(event);
         return;
     }
+
+    if (event->source() != Qt::MouseEventNotSynthesized)
+        return;
+
+    if (_mouseTouchPoint) {
+        updateTouchData(event);
+        _mouseTouchPoint->setPressed(false);
+        _mouseTouchPoint->setInUse(false);
+        _releasedTouchPoints.append(_mouseTouchPoint);
+        emit released(_releasedTouchPoints);
+        _releasedTouchPoints.removeAll(_mouseTouchPoint);
+        _mouseTouchPoint = Q_NULLPTR;
+    }
+
     QQuickWindow *c = window();
     if (c && c->mouseGrabberItem() == this)
         ungrabMouse();
index 83cc407..9dbca2b 100644 (file)
@@ -191,6 +191,7 @@ class Q_AUTOTEST_EXPORT QQuickMultiPointTouchArea : public QQuickItem
     Q_PROPERTY(QQmlListProperty<QQuickTouchPoint> touchPoints READ touchPoints)
     Q_PROPERTY(int minimumTouchPoints READ minimumTouchPoints WRITE setMinimumTouchPoints NOTIFY minimumTouchPointsChanged)
     Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints WRITE setMaximumTouchPoints NOTIFY maximumTouchPointsChanged)
+    Q_PROPERTY(bool mouseEnabled READ mouseEnabled WRITE setMouseEnabled NOTIFY mouseEnabledChanged)
 
 public:
     QQuickMultiPointTouchArea(QQuickItem *parent=0);
@@ -200,6 +201,8 @@ public:
     void setMinimumTouchPoints(int num);
     int maximumTouchPoints() const;
     void setMaximumTouchPoints(int num);
+    bool mouseEnabled() const { return _mouseEnabled; }
+    void setMouseEnabled(bool arg);
 
     QQmlListProperty<QQuickTouchPoint> touchPoints() {
         return QQmlListProperty<QQuickTouchPoint>(this, 0, QQuickMultiPointTouchArea::touchPoint_append, QQuickMultiPointTouchArea::touchPoint_count, QQuickMultiPointTouchArea::touchPoint_at, 0);
@@ -229,6 +232,7 @@ Q_SIGNALS:
     void touchUpdated(const QList<QObject*> &touchPoints);
     void minimumTouchPointsChanged();
     void maximumTouchPointsChanged();
+    void mouseEnabledChanged();
 
 protected:
     void touchEvent(QTouchEvent *);
@@ -241,9 +245,11 @@ protected:
 
     void addTouchPrototype(QQuickTouchPoint* prototype);
     void addTouchPoint(const QTouchEvent::TouchPoint *p);
+    void addTouchPoint(const QMouseEvent *e);
     void clearTouchLists();
 
     void updateTouchPoint(QQuickTouchPoint*, const QTouchEvent::TouchPoint*);
+    void updateTouchPoint(QQuickTouchPoint *dtp, const QMouseEvent *e);
     void updateTouchData(QEvent*);
 
     bool sendMouseEvent(QMouseEvent *event);
@@ -265,7 +271,11 @@ private:
     QList<QObject*> _movedTouchPoints;
     int _minimumTouchPoints;
     int _maximumTouchPoints;
+    QQuickTouchPoint *_mouseTouchPoint; // exists when mouse button is down and _mouseEnabled is true; null otherwise
+    QTouchEvent::TouchPoint _mouseQpaTouchPoint; // synthetic QPA touch point to hold state and position of the mouse
+    QPointF _mousePos;
     bool _stealMouse;
+    bool _mouseEnabled;
 };
 
 QT_END_NAMESPACE
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/dualGestures.qml b/tests/auto/quick/qquickmultipointtoucharea/data/dualGestures.qml
new file mode 100644 (file)
index 0000000..dfb667d
--- /dev/null
@@ -0,0 +1,94 @@
+/*  From the docs about minimumTouchPoints/maximumTouchPoints:
+    "...allow you to, for example, have nested MultiPointTouchAreas,
+    one handling two finger touches, and another handling three finger touches."
+    But in this test they are side-by-side: the left one handles any number
+    of touches up to 2, and the right one requires 3.
+*/
+import QtQuick 2.0
+
+Row {
+    width: 640
+    height: 480
+
+    Rectangle {
+        color: "black"
+        border.color: "white"
+        height: parent.height
+        width: parent.width / 2
+        MultiPointTouchArea {
+            objectName: "dualTouchArea"
+            anchors.fill: parent
+            maximumTouchPoints: 2
+            touchPoints: [
+                TouchPoint { id: touch1 },
+                TouchPoint { id: touch2 }
+            ]
+            Rectangle {
+                objectName: "touch1rect"
+                color: "red"
+                width: 30
+                height: width
+                radius: width / 2
+                x: touch1.x
+                y: touch1.y
+                border.color: touch1.pressed ? "white" : "transparent"
+            }
+            Rectangle {
+                objectName: "touch2rect"
+                color: "yellow"
+                width: 30
+                height: width
+                radius: width / 2
+                x: touch2.x
+                y: touch2.y
+                border.color: touch2.pressed ? "white" : "transparent"
+            }
+        }
+    }
+
+
+    Rectangle {
+        color: "black"
+        border.color: "white"
+        height: parent.height
+        width: parent.width / 2
+        MultiPointTouchArea {
+            objectName: "tripleTouchArea"
+            anchors.fill: parent
+            minimumTouchPoints: 3
+            maximumTouchPoints: 3
+            touchPoints: [
+                TouchPoint { id: touch3 },
+                TouchPoint { id: touch4 },
+                TouchPoint { id: touch5 }
+            ]
+            Rectangle {
+                objectName: "touch3rect"
+                color: "green"
+                width: 30
+                height: width
+                x: touch3.x
+                y: touch3.y
+                border.color: touch3.pressed ? "white" : "transparent"
+            }
+            Rectangle {
+                objectName: "touch4rect"
+                color: "blue"
+                width: 30
+                height: width
+                x: touch4.x
+                y: touch4.y
+                border.color: touch4.pressed ? "white" : "transparent"
+            }
+            Rectangle {
+                objectName: "touch5rect"
+                color: "violet"
+                width: 30
+                height: width
+                x: touch5.x
+                y: touch5.y
+                border.color: touch5.pressed ? "white" : "transparent"
+            }
+        }
+    }
+}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/inMouseArea.qml b/tests/auto/quick/qquickmultipointtoucharea/data/inMouseArea.qml
new file mode 100644 (file)
index 0000000..9c5d758
--- /dev/null
@@ -0,0 +1,30 @@
+import QtQuick 2.2
+
+MouseArea {
+    id: root
+    width: 240
+    height: 320
+    acceptedButtons: Qt.LeftButton | Qt.RightButton
+
+    Rectangle {
+        anchors.fill: parent
+        color: "black"
+        border.width: 5
+        border.color: parent.pressed ? "red" : "blue"
+
+        Rectangle {
+            anchors.fill: parent
+            anchors.margins: 25
+            color: mpta.pressed ? "orange" : "cyan"
+
+            MultiPointTouchArea {
+                id: mpta
+                objectName: "mpta"
+                property bool pressed: false
+                anchors.fill: parent
+                onPressed: pressed = true
+                onReleased: pressed = false
+            }
+        }
+    }
+}
diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml b/tests/auto/quick/qquickmultipointtoucharea/data/mouse.qml
new file mode 100644 (file)
index 0000000..0abcc76
--- /dev/null
@@ -0,0 +1,29 @@
+import QtQuick 2.0
+
+MultiPointTouchArea {
+    width: 240
+    height: 320
+
+    property int touchCount: 0
+    property int cancelCount: 0
+
+    minimumTouchPoints: 1
+    maximumTouchPoints: 4
+    touchPoints: [
+        TouchPoint { id: p1; objectName: "point1" },
+        TouchPoint { objectName: "point2" }
+    ]
+
+    onPressed: { touchCount = touchPoints.length }
+    onTouchUpdated: { touchCount = touchPoints.length }
+    onCanceled: { cancelCount = touchPoints.length }
+
+    Rectangle {
+        color: "red"
+        height: 30
+        width: 30
+        x: p1.x
+        y: p1.y
+    }
+
+}
index 2bddac4..1d4932c 100644 (file)
@@ -43,6 +43,7 @@
 #include <QtTest/QSignalSpy>
 #include <private/qquickmultipointtoucharea_p.h>
 #include <private/qquickflickable_p.h>
+#include <private/qquickmousearea_p.h>
 #include <qpa/qwindowsysteminterface.h>
 #include <QtQuick/qquickview.h>
 #include <QtGui/QScreen>
@@ -72,9 +73,13 @@ private slots:
     void nested();
     void inFlickable();
     void inFlickable2();
+    void inMouseArea();
+    void mouseAsTouchpoint();
     void invisible();
     void transformedTouchArea_data();
     void transformedTouchArea();
+    void mouseInteraction();
+    void mouseInteraction_data();
 
 private:
     QQuickView *createAndShowView(const QString &file);
@@ -550,6 +555,9 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
     QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(window->rootObject());
     QVERIFY(flickable != 0);
 
+    QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>();
+    QVERIFY(mpta != 0);
+
     QQuickTouchPoint *point11 = window->rootObject()->findChild<QQuickTouchPoint*>("point1");
     QQuickTouchPoint *point12 = window->rootObject()->findChild<QQuickTouchPoint*>("point2");
 
@@ -625,6 +633,8 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
     p1 = QPoint(20,100);
     p2 = QPoint(40,100);
     QTest::touchEvent(window.data(), device).press(0, p1).press(1, p2);
+    // ensure that mouse events do not fall through to the Flickable
+    mpta->setMaximumTouchPoints(3);
     QTest::mousePress(window.data(), Qt::LeftButton, 0, p1);
 
     QCOMPARE(point11->pressed(), true);
@@ -744,6 +754,239 @@ void tst_QQuickMultiPointTouchArea::inFlickable2()
     QTRY_VERIFY(!flickable->isMoving());
 }
 
+// QTBUG-31047
+void tst_QQuickMultiPointTouchArea::inMouseArea()
+{
+    QScopedPointer<QQuickView> window(createAndShowView("inMouseArea.qml"));
+    QVERIFY(window->rootObject() != 0);
+
+    QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea *>(window->rootObject());
+    QVERIFY(mouseArea != 0);
+
+    QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>("mpta");
+    QVERIFY(mpta != 0);
+
+    QPoint innerPoint(40,100);
+    QPoint outerPoint(10,100);
+
+    QTest::touchEvent(window.data(), device).press(0, innerPoint);
+    QVERIFY(mpta->property("pressed").toBool());
+    QTest::touchEvent(window.data(), device).release(0, innerPoint);
+    QVERIFY(!mpta->property("pressed").toBool());
+
+    QTest::mousePress(window.data(), Qt::LeftButton, 0, outerPoint);
+    QVERIFY(mouseArea->property("pressed").toBool());
+    QTest::mouseRelease(window.data(), Qt::LeftButton, 0, outerPoint);
+    QVERIFY(!mouseArea->property("pressed").toBool());
+
+    QTest::mousePress(window.data(), Qt::LeftButton, 0, innerPoint);
+    QVERIFY(mpta->property("pressed").toBool());
+    QVERIFY(!mouseArea->property("pressed").toBool());
+    QTest::mouseRelease(window.data(), Qt::LeftButton, 0, innerPoint);
+    QVERIFY(!mpta->property("pressed").toBool());
+    QVERIFY(!mouseArea->property("pressed").toBool());
+
+    QTest::touchEvent(window.data(), device).press(0, innerPoint);
+    QVERIFY(mpta->property("pressed").toBool());
+    QTest::touchEvent(window.data(), device).release(0, innerPoint);
+    QVERIFY(!mpta->property("pressed").toBool());
+
+    QTest::touchEvent(window.data(), device).press(0, outerPoint);
+    QVERIFY(mouseArea->property("pressed").toBool());
+    QVERIFY(!mpta->property("pressed").toBool());
+    QTest::touchEvent(window.data(), device).release(0, outerPoint);
+    QVERIFY(!mouseArea->property("pressed").toBool());
+    QVERIFY(!mpta->property("pressed").toBool());
+
+    // Right click should pass through
+    QTest::mousePress(window.data(), Qt::RightButton, 0, innerPoint);
+    QVERIFY(mouseArea->property("pressed").toBool());
+    QVERIFY(!mpta->property("pressed").toBool());
+    QTest::mouseRelease(window.data(), Qt::RightButton, 0, innerPoint);
+
+    mpta->setProperty("mouseEnabled", false);
+
+    QTest::touchEvent(window.data(), device).press(0, innerPoint);
+    QVERIFY(mpta->property("pressed").toBool());
+    QVERIFY(!mouseArea->property("pressed").toBool());
+    QTest::touchEvent(window.data(), device).release(0, innerPoint);
+    QVERIFY(!mpta->property("pressed").toBool());
+    QVERIFY(!mouseArea->property("pressed").toBool());
+
+    QTest::mousePress(window.data(), Qt::LeftButton, 0, outerPoint);
+    QVERIFY(mouseArea->property("pressed").toBool());
+    QTest::mouseRelease(window.data(), Qt::LeftButton, 0, outerPoint);
+    QVERIFY(!mouseArea->property("pressed").toBool());
+
+    QTest::mousePress(window.data(), Qt::LeftButton, 0, innerPoint);
+    QVERIFY(!mpta->property("pressed").toBool());
+    QVERIFY(mouseArea->property("pressed").toBool());
+    QTest::mouseRelease(window.data(), Qt::LeftButton, 0, innerPoint);
+    QVERIFY(!mpta->property("pressed").toBool());
+    QVERIFY(!mouseArea->property("pressed").toBool());
+
+    QTest::touchEvent(window.data(), device).press(0, innerPoint);
+    QVERIFY(mpta->property("pressed").toBool());
+    QTest::touchEvent(window.data(), device).release(0, innerPoint);
+    QVERIFY(!mpta->property("pressed").toBool());
+
+    QTest::touchEvent(window.data(), device).press(0, outerPoint);
+    QVERIFY(mouseArea->property("pressed").toBool());
+    QVERIFY(!mpta->property("pressed").toBool());
+    QTest::touchEvent(window.data(), device).release(0, outerPoint);
+    QVERIFY(!mouseArea->property("pressed").toBool());
+    QVERIFY(!mpta->property("pressed").toBool());
+}
+
+void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint()
+{
+    QScopedPointer<QQuickView> window(createAndShowView("dualGestures.qml"));
+    QVERIFY(window->rootObject() != 0);
+
+    QQuickMultiPointTouchArea *dualmpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>("dualTouchArea");
+    QVERIFY(dualmpta != 0);
+
+    QQuickItem *touch1rect = window->rootObject()->findChild<QQuickItem*>("touch1rect");
+    QQuickItem *touch2rect = window->rootObject()->findChild<QQuickItem*>("touch2rect");
+    QQuickItem *touch3rect = window->rootObject()->findChild<QQuickItem*>("touch3rect");
+    QQuickItem *touch4rect = window->rootObject()->findChild<QQuickItem*>("touch4rect");
+    QQuickItem *touch5rect = window->rootObject()->findChild<QQuickItem*>("touch5rect");
+
+    {
+        QPoint touch1(40,10);
+        QPoint touch2(40,100);
+        QPoint touch3(10,10);
+
+        // Touch both, release one, manipulate other touchpoint with mouse
+        QTest::touchEvent(window.data(), device).press(1, touch1);
+        QTest::touchEvent(window.data(), device).press(2, touch2);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+        QTest::touchEvent(window.data(), device).release(1, touch1);
+        touch1.setY(20);
+        QTest::mousePress(window.data(), Qt::LeftButton, 0, touch1);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+        QTest::touchEvent(window.data(), device).release(2, touch2);
+        QTest::mouseRelease(window.data(), Qt::LeftButton, 0, touch1);
+
+        // Start with mouse, move it, touch second point, move it
+        QTest::mousePress(window.data(), Qt::LeftButton, 0, touch1);
+        touch1.setX(60);
+        QTest::mouseMove(window.data(), touch1);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        touch2.setX(60);
+        QTest::touchEvent(window.data(), device).press(3, touch2);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+        touch2.setY(150);
+        QTest::touchEvent(window.data(), device).move(3, touch2);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+
+        // Touch third point - nothing happens
+        QTest::touchEvent(window.data(), device).press(4, touch3);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+
+        // Release all
+        QTest::mouseRelease(window.data(), Qt::LeftButton, 0, touch1);
+        QTest::touchEvent(window.data(), device).release(3, touch2);
+        QTest::touchEvent(window.data(), device).release(4, touch3);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+    }
+
+    // Mouse and touch on left, touch 3 points on right
+    {
+        QPoint mouse1(40,10);
+        QPoint touch1(10,10);
+        QPoint touch2(340,10);
+        QPoint touch3(340,100);
+        QPoint touch4(540,10);
+
+        QTest::mousePress(window.data(), Qt::LeftButton, 0, mouse1);
+        QCOMPARE(touch1rect->property("x").toInt(), mouse1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), mouse1.y());
+        QTest::touchEvent(window.data(), device).press(1, touch1);
+        QCOMPARE(touch1rect->property("x").toInt(), mouse1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), mouse1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch1.y());
+
+        QTest::touchEvent(window.data(), device).press(2, touch2).press(3, touch3).press(4, touch4);
+        QCOMPARE(touch1rect->property("x").toInt(), mouse1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), mouse1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch3rect->property("x").toInt(), touch2.x() - 320);
+        QCOMPARE(touch3rect->property("y").toInt(), touch2.y());
+        QCOMPARE(touch4rect->property("x").toInt(), touch3.x() - 320);
+        QCOMPARE(touch4rect->property("y").toInt(), touch3.y());
+        QCOMPARE(touch5rect->property("x").toInt(), touch4.x() - 320);
+        QCOMPARE(touch5rect->property("y").toInt(), touch4.y());
+
+        // Release all
+        QTest::mouseRelease(window.data(), Qt::LeftButton, 0, mouse1);
+        QTest::touchEvent(window.data(), device).release(1, touch1).release(2, touch2).release(3, touch3).release(4, touch4);
+    }
+
+    dualmpta->setProperty("mouseEnabled", false);
+    {
+        QPoint mouse1(40,10);
+        QPoint touch1(10,10);
+        QPoint touch2(100,10);
+
+        touch1rect->setX(10);
+        touch1rect->setY(10);
+        touch2rect->setX(20);
+        touch2rect->setY(10);
+
+        // Start with mouse, move it, touch a point, move it, touch another.
+        // Mouse is ignored, both touch points are heeded.
+        QTest::mousePress(window.data(), Qt::LeftButton, 0, mouse1);
+        mouse1.setX(60);
+        QTest::mouseMove(window.data(), mouse1);
+        QCOMPARE(touch1rect->property("x").toInt(), 10);
+        QCOMPARE(touch1rect->property("y").toInt(), 10);
+
+        QTest::touchEvent(window.data(), device).press(1, touch1);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        touch1.setY(150);
+        QTest::touchEvent(window.data(), device).move(1, touch1);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QTest::touchEvent(window.data(), device).press(2, touch2);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+
+        // Release all
+        QTest::mouseRelease(window.data(), Qt::LeftButton, 0, mouse1);
+        QTest::touchEvent(window.data(), device).release(1, touch1);
+        QTest::touchEvent(window.data(), device).release(2, touch2);
+        QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
+        QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
+        QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
+        QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
+    }
+}
+
 // QTBUG-23327
 void tst_QQuickMultiPointTouchArea::invisible()
 {
@@ -841,6 +1084,48 @@ QQuickView *tst_QQuickMultiPointTouchArea::createAndShowView(const QString &file
     return window;
 }
 
+void tst_QQuickMultiPointTouchArea::mouseInteraction_data()
+{
+    QTest::addColumn<int>("buttons");
+    QTest::addColumn<int>("accept");
+
+    QTest::newRow("left") << (int) Qt::LeftButton << 1;
+    QTest::newRow("right") << (int) Qt::RightButton << 0;
+    QTest::newRow("middle") << (int) Qt::MiddleButton << 0;
+}
+
+void tst_QQuickMultiPointTouchArea::mouseInteraction()
+{
+    QFETCH(int, buttons);
+    QFETCH(int, accept);
+
+    QScopedPointer<QQuickView> view(createAndShowView("mouse.qml"));
+    QVERIFY(view->rootObject() != 0);
+
+    QQuickMultiPointTouchArea *area = qobject_cast<QQuickMultiPointTouchArea *>(view->rootObject());
+    QVERIFY(area != 0);
+    QQuickTouchPoint *point1 = view->rootObject()->findChild<QQuickTouchPoint*>("point1");
+    QCOMPARE(point1->pressed(), false);
+
+    QCOMPARE(area->property("touchCount").toInt(), 0);
+    QPoint p1 = QPoint(100, 100);
+    QTest::mousePress(view.data(), (Qt::MouseButton) buttons, 0, p1);
+    QCOMPARE(area->property("touchCount").toInt(), accept);
+    QCOMPARE(point1->pressed(), accept);
+    p1 += QPoint(10, 10);
+    QTest::mouseMove(view.data(), p1);
+    QCOMPARE(point1->pressed(), accept);
+    QCOMPARE(area->property("touchCount").toInt(), accept);
+    p1 += QPoint(10, 10);
+    QTest::mouseMove(view.data(), p1);
+    QCOMPARE(point1->pressed(), accept);
+    QCOMPARE(area->property("touchCount").toInt(), accept);
+    QTest::mouseRelease(view.data(), (Qt::MouseButton) buttons);
+    QCOMPARE(point1->pressed(), false);
+    QCOMPARE(area->property("touchCount").toInt(), 0);
+}
+
+
 QTEST_MAIN(tst_QQuickMultiPointTouchArea)
 
 #include "tst_qquickmultipointtoucharea.moc"