From feb996e3ab44e68082c97102556ea396f5df3f44 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 11 Jul 2012 17:32:16 +1000 Subject: QQuickCanvas renames QQuickCanvas is now called QQuickWindow QQuickCanvas::rootItem is now QQuickWindow::contentItem QQuickItem::canvas is now QQuickItem::window QQuickItem::ItemChangeData::canvas is also renamed window QQuickCanvas::grabFrameBuffer is now QQuickWindow::grabWindow The functions related to the color property have dropped the clear from their names. The first three changes have interim compatibility measures in place to ease the transition. Change-Id: Id34e29546a22a74a7ae2ad90ee3a8def6fc541d2 Reviewed-by: Martin Jones --- .../qquickwindow/data/AnimationsWhileHidden.qml | 17 + tests/auto/quick/qquickwindow/data/Headless.qml | 33 + tests/auto/quick/qquickwindow/data/colors.png | Bin 0 -> 1655 bytes tests/auto/quick/qquickwindow/data/focus.qml | 11 + .../quick/qquickwindow/data/ownershipRootItem.qml | 11 + tests/auto/quick/qquickwindow/data/window.qml | 9 + tests/auto/quick/qquickwindow/qquickwindow.pro | 17 + tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 1065 ++++++++++++++++++++ 8 files changed, 1163 insertions(+) create mode 100644 tests/auto/quick/qquickwindow/data/AnimationsWhileHidden.qml create mode 100644 tests/auto/quick/qquickwindow/data/Headless.qml create mode 100644 tests/auto/quick/qquickwindow/data/colors.png create mode 100644 tests/auto/quick/qquickwindow/data/focus.qml create mode 100644 tests/auto/quick/qquickwindow/data/ownershipRootItem.qml create mode 100644 tests/auto/quick/qquickwindow/data/window.qml create mode 100644 tests/auto/quick/qquickwindow/qquickwindow.pro create mode 100644 tests/auto/quick/qquickwindow/tst_qquickwindow.cpp (limited to 'tests/auto/quick/qquickwindow') diff --git a/tests/auto/quick/qquickwindow/data/AnimationsWhileHidden.qml b/tests/auto/quick/qquickwindow/data/AnimationsWhileHidden.qml new file mode 100644 index 0000000000..e95b029210 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/AnimationsWhileHidden.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 as Window + +Window.Window +{ + id: win + visible: true + width: 250 + height: 250 + + SequentialAnimation { + PauseAnimation { duration: 500 } + PropertyAction { target: win; property: "visible"; value: true } + loops: Animation.Infinite + running: true + } +} diff --git a/tests/auto/quick/qquickwindow/data/Headless.qml b/tests/auto/quick/qquickwindow/data/Headless.qml new file mode 100644 index 0000000000..2e09cb1f24 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/Headless.qml @@ -0,0 +1,33 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 as Window + +Window.Window { + + width: 300 + height: 200 + visible: true + + Text { + anchors.left: parent.left + anchors.top: parent.top + text: "Testing headless mode" + } + + Rectangle { + anchors.centerIn: parent + width: 100 + height: 50 + rotation: -30 + gradient: Gradient { + GradientStop { position: 0; color: "lightsteelblue" } + GradientStop { position: 1; color: "black" } + } + } + + Image { + source: "colors.png" + anchors.bottom: parent.bottom + anchors.right: parent.right + } + +} diff --git a/tests/auto/quick/qquickwindow/data/colors.png b/tests/auto/quick/qquickwindow/data/colors.png new file mode 100644 index 0000000000..dfb62f3d64 Binary files /dev/null and b/tests/auto/quick/qquickwindow/data/colors.png differ diff --git a/tests/auto/quick/qquickwindow/data/focus.qml b/tests/auto/quick/qquickwindow/data/focus.qml new file mode 100644 index 0000000000..901f2fcf2e --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/focus.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 as Window + +Window.Window { + Item { + objectName: "item1" + } + Item { + objectName: "item2" + } +} diff --git a/tests/auto/quick/qquickwindow/data/ownershipRootItem.qml b/tests/auto/quick/qquickwindow/data/ownershipRootItem.qml new file mode 100644 index 0000000000..dfc4159f4e --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/ownershipRootItem.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 as Window + +Window.Window { +RootItemAccessor { + id:accessor + objectName:"accessor" + Component.onCompleted:accessor.rootItem(); +} + +} \ No newline at end of file diff --git a/tests/auto/quick/qquickwindow/data/window.qml b/tests/auto/quick/qquickwindow/data/window.qml new file mode 100644 index 0000000000..d79d5161b5 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/window.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 as Window + +Window.Window { + color: "#00FF00" + Item { + objectName: "item" + } +} diff --git a/tests/auto/quick/qquickwindow/qquickwindow.pro b/tests/auto/quick/qquickwindow/qquickwindow.pro new file mode 100644 index 0000000000..8f3ff82ceb --- /dev/null +++ b/tests/auto/quick/qquickwindow/qquickwindow.pro @@ -0,0 +1,17 @@ +CONFIG += testcase +TARGET = tst_qquickwindow +SOURCES += tst_qquickwindow.cpp + +include (../../shared/util.pri) + +macx:CONFIG -= app_bundle + +CONFIG += parallel_test +QT += core-private gui-private qml-private quick-private v8-private testlib + +TESTDATA = data/* + +OTHER_FILES += \ + data/AnimationsWhileHidden.qml \ + data/Headless.qml + diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp new file mode 100644 index 0000000000..ebea271565 --- /dev/null +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -0,0 +1,1065 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../shared/util.h" +#include +#include +#include + +struct TouchEventData { + QEvent::Type type; + QWidget *widget; + QWindow *window; + Qt::TouchPointStates states; + QList touchPoints; +}; + +static QTouchEvent::TouchPoint makeTouchPoint(QQuickItem *item, const QPointF &p, const QPointF &lastPoint = QPointF()) +{ + QPointF last = lastPoint.isNull() ? p : lastPoint; + + QTouchEvent::TouchPoint tp; + + tp.setPos(p); + tp.setLastPos(last); + tp.setScenePos(item->mapToScene(p)); + tp.setLastScenePos(item->mapToScene(last)); + tp.setScreenPos(item->window()->mapToGlobal(tp.scenePos().toPoint())); + tp.setLastScreenPos(item->window()->mapToGlobal(tp.lastScenePos().toPoint())); + return tp; +} + +static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states = 0, + const QList& touchPoints = QList()) +{ + TouchEventData d = { type, 0, w, states, touchPoints }; + return d; +} +static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint) +{ + QList points; + points << touchPoint; + return makeTouchData(type, w, states, points); +} + +#define COMPARE_TOUCH_POINTS(tp1, tp2) \ +{ \ + QCOMPARE(tp1.pos(), tp2.pos()); \ + QCOMPARE(tp1.lastPos(), tp2.lastPos()); \ + QCOMPARE(tp1.scenePos(), tp2.scenePos()); \ + QCOMPARE(tp1.lastScenePos(), tp2.lastScenePos()); \ + QCOMPARE(tp1.screenPos(), tp2.screenPos()); \ + QCOMPARE(tp1.lastScreenPos(), tp2.lastScreenPos()); \ +} + +#define COMPARE_TOUCH_DATA(d1, d2) \ +{ \ + QCOMPARE((int)d1.type, (int)d2.type); \ + QCOMPARE(d1.widget, d2.widget); \ + QCOMPARE((int)d1.states, (int)d2.states); \ + QCOMPARE(d1.touchPoints.count(), d2.touchPoints.count()); \ + for (int i=0; irootItem; + QObject::connect(m_rootItem, SIGNAL(destroyed()), this, SLOT(rootItemDestroyed())); + } + return m_rootItem; + } + bool isRootItemDestroyed() {return m_rootItemDestroyed;} +public slots: + void rootItemDestroyed() { + m_rootItemDestroyed = true; + } + +private: + bool m_rootItemDestroyed; + QQuickItem *m_rootItem; +}; + +class TestTouchItem : public QQuickRectangle +{ + Q_OBJECT +public: + TestTouchItem(QQuickItem *parent = 0) + : QQuickRectangle(parent), acceptTouchEvents(true), acceptMouseEvents(true), + mousePressId(0), + spinLoopWhenPressed(false), touchEventCount(0) + { + border()->setWidth(1); + setAcceptedMouseButtons(Qt::LeftButton); + setFiltersChildMouseEvents(true); + } + + void reset() { + acceptTouchEvents = acceptMouseEvents = true; + setEnabled(true); + setVisible(true); + + lastEvent = makeTouchData(QEvent::None, window(), 0, QList());//CHECK_VALID + + lastVelocity = lastVelocityFromMouseMove = QVector2D(); + lastMousePos = QPointF(); + lastMouseCapabilityFlags = 0; + } + + static void clearMousePressCounter() + { + mousePressNum = mouseMoveNum = mouseReleaseNum = 0; + } + + void clearTouchEventCounter() + { + touchEventCount = 0; + } + + bool acceptTouchEvents; + bool acceptMouseEvents; + TouchEventData lastEvent; + int mousePressId; + bool spinLoopWhenPressed; + int touchEventCount; + QVector2D lastVelocity; + QVector2D lastVelocityFromMouseMove; + QPointF lastMousePos; + int lastMouseCapabilityFlags; + + void touchEvent(QTouchEvent *event) { + if (!acceptTouchEvents) { + event->ignore(); + return; + } + ++touchEventCount; + lastEvent = makeTouchData(event->type(), event->window(), event->touchPointStates(), event->touchPoints()); + if (event->device()->capabilities().testFlag(QTouchDevice::Velocity) && !event->touchPoints().isEmpty()) { + lastVelocity = event->touchPoints().first().velocity(); + } else { + lastVelocity = QVector2D(); + } + if (spinLoopWhenPressed && event->touchPointStates().testFlag(Qt::TouchPointPressed)) { + QCoreApplication::processEvents(); + } + } + + void mousePressEvent(QMouseEvent *e) { + if (!acceptMouseEvents) { + e->ignore(); + return; + } + mousePressId = ++mousePressNum; + lastMousePos = e->pos(); + lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); + } + + void mouseMoveEvent(QMouseEvent *e) { + if (!acceptMouseEvents) { + e->ignore(); + return; + } + ++mouseMoveNum; + lastVelocityFromMouseMove = QGuiApplicationPrivate::mouseEventVelocity(e); + lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); + lastMousePos = e->pos(); + } + + void mouseReleaseEvent(QMouseEvent *e) { + if (!acceptMouseEvents) { + e->ignore(); + return; + } + ++mouseReleaseNum; + lastMousePos = e->pos(); + lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); + } + + bool childMouseEventFilter(QQuickItem *, QEvent *event) { + // TODO Is it a bug if a QTouchEvent comes here? + if (event->type() == QEvent::MouseButtonPress) + mousePressId = ++mousePressNum; + return false; + } + + static int mousePressNum, mouseMoveNum, mouseReleaseNum; +}; + +int TestTouchItem::mousePressNum = 0; +int TestTouchItem::mouseMoveNum = 0; +int TestTouchItem::mouseReleaseNum = 0; + +class ConstantUpdateItem : public QQuickItem +{ +Q_OBJECT +public: + ConstantUpdateItem(QQuickItem *parent = 0) : QQuickItem(parent), iterations(0) {setFlag(ItemHasContents);} + + int iterations; +protected: + QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){ + iterations++; + update(); + return 0; + } +}; + +class tst_qquickwindow : public QQmlDataTest +{ + Q_OBJECT +public: + +private slots: + void initTestCase() + { + QQmlDataTest::initTestCase(); + touchDevice = new QTouchDevice; + touchDevice->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(touchDevice); + touchDeviceWithVelocity = new QTouchDevice; + touchDeviceWithVelocity->setType(QTouchDevice::TouchScreen); + touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity); + QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity); + } + + + void constantUpdates(); + void mouseFiltering(); + void headless(); + + void touchEvent_basic(); + void touchEvent_propagation(); + void touchEvent_propagation_data(); + void touchEvent_cancel(); + void touchEvent_reentrant(); + void touchEvent_velocity(); + + void mouseFromTouch_basic(); + + void clearWindow(); + + void qmlCreation(); + void clearColor(); + + void grab(); + void multipleWindows(); + + void animationsWhileHidden(); + + void focusObject(); + + void ignoreUnhandledMouseEvents(); + + void ownershipRootItem(); +private: + QTouchDevice *touchDevice; + QTouchDevice *touchDeviceWithVelocity; +}; + +//If the item calls update inside updatePaintNode, it should schedule another update +void tst_qquickwindow::constantUpdates() +{ + QQuickWindow window; + window.resize(250, 250); + ConstantUpdateItem item(window.rootItem()); + window.show(); + QTRY_VERIFY(item.iterations > 60); +} + +void tst_qquickwindow::touchEvent_basic() +{ + TestTouchItem::clearMousePressCounter(); + + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + + TestTouchItem *bottomItem = new TestTouchItem(window->rootItem()); + bottomItem->setObjectName("Bottom Item"); + bottomItem->setSize(QSizeF(150, 150)); + + TestTouchItem *middleItem = new TestTouchItem(bottomItem); + middleItem->setObjectName("Middle Item"); + middleItem->setPos(QPointF(50, 50)); + middleItem->setSize(QSizeF(150, 150)); + + TestTouchItem *topItem = new TestTouchItem(middleItem); + topItem->setObjectName("Top Item"); + topItem->setPos(QPointF(50, 50)); + topItem->setSize(QSizeF(150, 150)); + + QPointF pos(10, 10); + + // press single point + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window); + QTest::qWait(50); + + QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); + // At one point this was failing with kwin (KDE window manager) because window->setPos(100, 100) + // would put the decorated window at that position rather than the window itself. + COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos))); + topItem->reset(); + + // press multiple points + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window) + .press(1, bottomItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); + COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos))); + COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos))); + topItem->reset(); + bottomItem->reset(); + + // touch point on top item moves to bottom item, but top item should still receive the event + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, + makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos))); + topItem->reset(); + + // touch point on bottom item moves to top item, but bottom item should still receive the event + QTest::touchEvent(window, touchDevice).press(0, bottomItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QTest::touchEvent(window, touchDevice).move(0, topItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); + COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, + makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos))); + bottomItem->reset(); + + // a single stationary press on an item shouldn't cause an event + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QTest::touchEvent(window, touchDevice).stationary(0) + .press(1, bottomItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); // received press only, not stationary + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); + COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos))); + COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos))); + topItem->reset(); + bottomItem->reset(); + // cleanup: what is pressed must be released + // Otherwise you will get an assertion failure: + // ASSERT: "itemForTouchPointId.isEmpty()" in file items/qquickwindow.cpp + QTest::touchEvent(window, touchDevice).release(0, pos.toPoint(), window).release(1, pos.toPoint(), window); + + // move touch point from top item to bottom, and release + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window); + QTest::qWait(50); + QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(),window); + QTest::qWait(50); + QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, window, Qt::TouchPointReleased, + makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos))); + topItem->reset(); + + // release while another point is pressed + QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window) + .press(1, bottomItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window); + QTest::qWait(50); + QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window) + .stationary(1); + QTest::qWait(50); + QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); + COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, window, Qt::TouchPointReleased, + makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos)))); + COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos))); + topItem->reset(); + bottomItem->reset(); + + delete topItem; + delete middleItem; + delete bottomItem; + delete window; +} + +void tst_qquickwindow::touchEvent_propagation() +{ + TestTouchItem::clearMousePressCounter(); + + QFETCH(bool, acceptTouchEvents); + QFETCH(bool, acceptMouseEvents); + QFETCH(bool, enableItem); + QFETCH(bool, showItem); + + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + + TestTouchItem *bottomItem = new TestTouchItem(window->rootItem()); + bottomItem->setObjectName("Bottom Item"); + bottomItem->setSize(QSizeF(150, 150)); + + TestTouchItem *middleItem = new TestTouchItem(bottomItem); + middleItem->setObjectName("Middle Item"); + middleItem->setPos(QPointF(50, 50)); + middleItem->setSize(QSizeF(150, 150)); + + TestTouchItem *topItem = new TestTouchItem(middleItem); + topItem->setObjectName("Top Item"); + topItem->setPos(QPointF(50, 50)); + topItem->setSize(QSizeF(150, 150)); + + QPointF pos(10, 10); + QPoint pointInBottomItem = bottomItem->mapToScene(pos).toPoint(); // (10, 10) + QPoint pointInMiddleItem = middleItem->mapToScene(pos).toPoint(); // (60, 60) overlaps with bottomItem + QPoint pointInTopItem = topItem->mapToScene(pos).toPoint(); // (110, 110) overlaps with bottom & top items + + // disable topItem + topItem->acceptTouchEvents = acceptTouchEvents; + topItem->acceptMouseEvents = acceptMouseEvents; + topItem->setEnabled(enableItem); + topItem->setVisible(showItem); + + // single touch to top item, should be received by middle item + QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window); + QTest::qWait(50); + QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); + QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1); + QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); + COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, + makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)))); + + // touch top and middle items, middle item should get both events + QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) + .press(1, pointInMiddleItem, window); + QTest::qWait(50); + QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); + QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2); + QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); + COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, + (QList() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)) + << makeTouchPoint(middleItem, pos) ))); + middleItem->reset(); + + // disable middleItem as well + middleItem->acceptTouchEvents = acceptTouchEvents; + middleItem->acceptMouseEvents = acceptMouseEvents; + middleItem->setEnabled(enableItem); + middleItem->setVisible(showItem); + + // touch top and middle items, bottom item should get all events + QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) + .press(1, pointInMiddleItem, window); + QTest::qWait(50); + QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2); + COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, + (QList() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos)) + << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) ))); + bottomItem->reset(); + + // disable bottom item as well + bottomItem->acceptTouchEvents = acceptTouchEvents; + bottomItem->setEnabled(enableItem); + bottomItem->setVisible(showItem); + + // no events should be received + QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) + .press(1, pointInMiddleItem, window) + .press(2, pointInBottomItem, window); + QTest::qWait(50); + QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); + + topItem->reset(); + middleItem->reset(); + bottomItem->reset(); + + // disable middle item, touch on top item + middleItem->acceptTouchEvents = acceptTouchEvents; + middleItem->setEnabled(enableItem); + middleItem->setVisible(showItem); + QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window); + QTest::qWait(50); + if (!enableItem || !showItem) { + // middle item is disabled or has 0 opacity, bottom item receives the event + QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); + COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, + makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos)))); + } else { + // middle item ignores event, sends it to the top item (top-most child) + QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); + QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); + QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); + COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, + makeTouchPoint(topItem, pos))); + } + + delete topItem; + delete middleItem; + delete bottomItem; + delete window; +} + +void tst_qquickwindow::touchEvent_propagation_data() +{ + QTest::addColumn("acceptTouchEvents"); + QTest::addColumn("acceptMouseEvents"); + QTest::addColumn("enableItem"); + QTest::addColumn("showItem"); + + QTest::newRow("disable events") << false << false << true << true; + QTest::newRow("disable item") << true << true << false << true; + QTest::newRow("hide item") << true << true << true << false; +} + +void tst_qquickwindow::touchEvent_cancel() +{ + TestTouchItem::clearMousePressCounter(); + + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + + TestTouchItem *item = new TestTouchItem(window->rootItem()); + item->setPos(QPointF(50, 50)); + item->setSize(QSizeF(150, 150)); + + QPointF pos(10, 10); + QTest::touchEvent(window, touchDevice).press(0, item->mapToScene(pos).toPoint(),window); + QCoreApplication::processEvents(); + + QTRY_COMPARE(item->lastEvent.touchPoints.count(), 1); + TouchEventData d = makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(item,pos)); + COMPARE_TOUCH_DATA(item->lastEvent, d); + item->reset(); + + QWindowSystemInterface::handleTouchCancelEvent(0, touchDevice); + QCoreApplication::processEvents(); + d = makeTouchData(QEvent::TouchCancel, window); + COMPARE_TOUCH_DATA(item->lastEvent, d); + + delete item; + delete window; +} + +void tst_qquickwindow::touchEvent_reentrant() +{ + TestTouchItem::clearMousePressCounter(); + + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + + TestTouchItem *item = new TestTouchItem(window->rootItem()); + + item->spinLoopWhenPressed = true; // will call processEvents() from the touch handler + + item->setPos(QPointF(50, 50)); + item->setSize(QSizeF(150, 150)); + QPointF pos(60, 60); + + // None of these should commit from the dtor. + QTest::QTouchEventSequence press = QTest::touchEvent(window, touchDevice, false).press(0, pos.toPoint(), window); + pos += QPointF(2, 2); + QTest::QTouchEventSequence move = QTest::touchEvent(window, touchDevice, false).move(0, pos.toPoint(), window); + QTest::QTouchEventSequence release = QTest::touchEvent(window, touchDevice, false).release(0, pos.toPoint(), window); + + // Now commit (i.e. call QWindowSystemInterface::handleTouchEvent), but do not process the events yet. + press.commit(false); + move.commit(false); + release.commit(false); + + QCoreApplication::processEvents(); + + QTRY_COMPARE(item->touchEventCount, 3); + + delete item; + delete window; +} + +void tst_qquickwindow::touchEvent_velocity() +{ + TestTouchItem::clearMousePressCounter(); + + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + QTest::qWait(10); + + TestTouchItem *item = new TestTouchItem(window->rootItem()); + item->setPos(QPointF(50, 50)); + item->setSize(QSizeF(150, 150)); + + QList points; + QWindowSystemInterface::TouchPoint tp; + tp.id = 1; + tp.state = Qt::TouchPointPressed; + QPoint pos = window->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint()); + tp.area = QRectF(pos, QSizeF(4, 4)); + points << tp; + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + points[0].state = Qt::TouchPointMoved; + points[0].area.adjust(5, 5, 5, 5); + QVector2D velocity(1.5, 2.5); + points[0].velocity = velocity; + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QCoreApplication::processEvents(); + QCOMPARE(item->touchEventCount, 2); + QCOMPARE(item->lastEvent.touchPoints.count(), 1); + QCOMPARE(item->lastVelocity, velocity); + + // Now have a transformation on the item and check if velocity and position are transformed accordingly. + item->setRotation(90); // clockwise + QMatrix4x4 transformMatrix; + transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise + QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D(); + points[0].area.adjust(5, 5, 5, 5); + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QCoreApplication::processEvents(); + QCOMPARE(item->lastVelocity, transformedVelocity); + QPoint itemLocalPos = item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint(); + QPoint itemLocalPosFromEvent = item->lastEvent.touchPoints[0].pos().toPoint(); + QCOMPARE(itemLocalPos, itemLocalPosFromEvent); + + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QCoreApplication::processEvents(); + delete item; + delete window; +} + +void tst_qquickwindow::mouseFromTouch_basic() +{ + // Turn off accepting touch events with acceptTouchEvents. This + // should result in sending mouse events generated from the touch + // with the new event propagation system. + + TestTouchItem::clearMousePressCounter(); + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + QTest::qWait(10); + + TestTouchItem *item = new TestTouchItem(window->rootItem()); + item->setPos(QPointF(50, 50)); + item->setSize(QSizeF(150, 150)); + item->acceptTouchEvents = false; + + QList points; + QWindowSystemInterface::TouchPoint tp; + tp.id = 1; + tp.state = Qt::TouchPointPressed; + QPoint pos = window->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint()); + tp.area = QRectF(pos, QSizeF(4, 4)); + points << tp; + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + points[0].state = Qt::TouchPointMoved; + points[0].area.adjust(5, 5, 5, 5); + QVector2D velocity(1.5, 2.5); + points[0].velocity = velocity; + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QCoreApplication::processEvents(); + + // The item should have received a mouse press, move, and release. + QCOMPARE(item->mousePressNum, 1); + QCOMPARE(item->mouseMoveNum, 1); + QCOMPARE(item->mouseReleaseNum, 1); + QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint()); + QCOMPARE(item->lastVelocityFromMouseMove, velocity); + QVERIFY((item->lastMouseCapabilityFlags & QTouchDevice::Velocity) != 0); + + // Now the same with a transformation. + item->setRotation(90); // clockwise + QMatrix4x4 transformMatrix; + transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise + QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D(); + points[0].state = Qt::TouchPointPressed; + points[0].velocity = velocity; + points[0].area = QRectF(pos, QSizeF(4, 4)); + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + points[0].state = Qt::TouchPointMoved; + points[0].area.adjust(5, 5, 5, 5); + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QCoreApplication::processEvents(); + QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint()); + QCOMPARE(item->lastVelocityFromMouseMove, transformedVelocity); + + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QCoreApplication::processEvents(); + delete item; + delete window; +} + +void tst_qquickwindow::clearWindow() +{ + QQuickWindow *window = new QQuickWindow; + QQuickItem *item = new QQuickItem; + item->setParentItem(window->rootItem()); + + QVERIFY(item->window() == window); + + delete window; + + QVERIFY(item->window() == 0); + + delete item; +} + +void tst_qquickwindow::mouseFiltering() +{ + TestTouchItem::clearMousePressCounter(); + + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + + TestTouchItem *bottomItem = new TestTouchItem(window->rootItem()); + bottomItem->setObjectName("Bottom Item"); + bottomItem->setSize(QSizeF(150, 150)); + + TestTouchItem *middleItem = new TestTouchItem(bottomItem); + middleItem->setObjectName("Middle Item"); + middleItem->setPos(QPointF(50, 50)); + middleItem->setSize(QSizeF(150, 150)); + + TestTouchItem *topItem = new TestTouchItem(middleItem); + topItem->setObjectName("Top Item"); + topItem->setPos(QPointF(50, 50)); + topItem->setSize(QSizeF(150, 150)); + + QPoint pos(100, 100); + + QTest::mousePress(window, Qt::LeftButton, 0, pos); + + // Mouse filtering propagates down the stack, so the + // correct order is + // 1. middleItem filters event + // 2. bottomItem filters event + // 3. topItem receives event + QTRY_COMPARE(middleItem->mousePressId, 1); + QTRY_COMPARE(bottomItem->mousePressId, 2); + QTRY_COMPARE(topItem->mousePressId, 3); + + delete window; +} + +void tst_qquickwindow::qmlCreation() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("window.qml")); + QObject* created = component.create(); + QVERIFY(created); + + QQuickWindow* window = qobject_cast(created); + QVERIFY(window); + QCOMPARE(window->color(), QColor(Qt::green)); + + QQuickItem* item = window->findChild("item"); + QVERIFY(item); + QCOMPARE(item->window(), window); + + delete window; +} + +void tst_qquickwindow::clearColor() +{ + //::grab examines rendering to make sure it works visually + QQuickWindow *window = new QQuickWindow; + window->resize(250, 250); + window->setPos(100, 100); + window->setColor(Qt::blue); + window->show(); + QTest::qWaitForWindowShown(window); + QCOMPARE(window->color(), QColor(Qt::blue)); + delete window; +} + +void tst_qquickwindow::grab() +{ + QQuickWindow window; + window.setColor(Qt::red); + + window.resize(250, 250); + window.show(); + + QTest::qWaitForWindowShown(&window); + + QImage content = window.grabWindow(); + QCOMPARE(content.width(), window.width()); + QCOMPARE(content.height(), window.height()); + QCOMPARE((uint) content.convertToFormat(QImage::Format_RGB32).pixel(0, 0), (uint) 0xffff0000); +} + +void tst_qquickwindow::multipleWindows() +{ + QList windows; + for (int i=0; i<6; ++i) { + QQuickWindow *c = new QQuickWindow(); + c->setColor(Qt::GlobalColor(Qt::red + i)); + c->resize(300, 200); + c->setPos(100 + i * 30, 100 + i * 20); + c->show(); + windows << c; + QVERIFY(c->isVisible()); + } + + // move them + for (int i=0; isetPos(c->x() - 10, c->y() - 10); + } + + // resize them + for (int i=0; iresize(200, 150); + } + + qDeleteAll(windows); +} + +void tst_qquickwindow::animationsWhileHidden() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("AnimationsWhileHidden.qml")); + QObject* created = component.create(); + + QQuickWindow* window = qobject_cast(created); + QVERIFY(window); + QVERIFY(window->isVisible()); + + // Now hide the window and verify that it went off screen + window->hide(); + QTest::qWait(10); + QVERIFY(!window->isVisible()); + + // Running animaiton should cause it to become visible again shortly. + QTRY_VERIFY(window->isVisible()); + + delete window; +} + + +void tst_qquickwindow::headless() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("Headless.qml")); + QObject* created = component.create(); + + QQuickWindow* window = qobject_cast(created); + QVERIFY(window); + + QTest::qWaitForWindowShown(window); + QVERIFY(window->isVisible()); + + QSignalSpy initialized(window, SIGNAL(sceneGraphInitialized())); + QSignalSpy invalidated(window, SIGNAL(sceneGraphInvalidated())); + + // Verify that the window is alive and kicking + QVERIFY(window->openglContext() != 0); + + // Store the visual result + QImage originalContent = window->grabWindow(); + + // Hide the window and verify signal emittion and GL context deletion + window->hide(); + window->releaseResources(); + + QTRY_COMPARE(invalidated.size(), 1); + QVERIFY(window->openglContext() == 0); + + // Destroy the native windowing system buffers + window->destroy(); + QVERIFY(window->handle() == 0); + + // Show and verify that we are back and running + window->show(); + QTest::qWaitForWindowShown(window); + + QTRY_COMPARE(initialized.size(), 1); + QVERIFY(window->openglContext() != 0); + + // Verify that the visual output is the same + QImage newContent = window->grabWindow(); + + QCOMPARE(originalContent, newContent); + + delete window; +} + +void tst_qquickwindow::focusObject() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("focus.qml")); + QObject *created = component.create(); + QVERIFY(created); + + QQuickWindow *window = qobject_cast(created); + QVERIFY(window); + + QQuickItem *item1 = window->findChild("item1"); + QVERIFY(item1); + item1->setFocus(true); + QCOMPARE(item1, window->focusObject()); + + QQuickItem *item2 = window->findChild("item2"); + QVERIFY(item2); + item2->setFocus(true); + QCOMPARE(item2, window->focusObject()); + + delete window; +} + +void tst_qquickwindow::ignoreUnhandledMouseEvents() +{ + QQuickWindow* window = new QQuickWindow; + window->resize(100, 100); + window->show(); + QTest::qWaitForWindowShown(window); + + QQuickItem* item = new QQuickItem; + item->setSize(QSizeF(100, 100)); + item->setParentItem(window->rootItem()); + + { + QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier); + me.setAccepted(true); + QVERIFY(QCoreApplication::sendEvent(window, &me)); + QVERIFY(!me.isAccepted()); + } + + { + QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier); + me.setAccepted(true); + QVERIFY(QCoreApplication::sendEvent(window, &me)); + QVERIFY(!me.isAccepted()); + } + + { + QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier); + me.setAccepted(true); + QVERIFY(QCoreApplication::sendEvent(window, &me)); + QVERIFY(!me.isAccepted()); + } + + delete window; +} + + +void tst_qquickwindow::ownershipRootItem() +{ + qmlRegisterType("QtQuick", 2, 0, "RootItemAccessor"); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("ownershipRootItem.qml")); + QObject* created = component.create(); + + QQuickWindow* window = qobject_cast(created); + QVERIFY(window); + QTest::qWaitForWindowShown(window); + + RootItemAccessor* accessor = window->findChild("accessor"); + QVERIFY(accessor); + engine.collectGarbage(); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QVERIFY(!accessor->isRootItemDestroyed()); +} +QTEST_MAIN(tst_qquickwindow) + +#include "tst_qquickwindow.moc" -- cgit v1.2.3