diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2016-12-20 09:37:14 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2016-12-20 09:37:14 +0100 |
commit | c07610b208268e6b6b952d634a6540ff66a0a8a8 (patch) | |
tree | df8f0a71db8e1d3a6144e35468125c2b263dc372 /src/imports | |
parent | 6f94828e8f1865259ff1b1cd7fda5064ffd9576c (diff) | |
parent | c4eefa4a8d6d3e95062deb78229940460a7ef605 (diff) |
Merge branch remote-tracking branch 'origin/dev' into wip/pointerhandler
Change-Id: I9ed2e696108f11c9153012fcf092541fd0e0d7c8
Diffstat (limited to 'src/imports')
-rw-r--r-- | src/imports/folderlistmodel/fileinfothread.cpp | 16 | ||||
-rw-r--r-- | src/imports/folderlistmodel/fileinfothread_p.h | 4 | ||||
-rw-r--r-- | src/imports/folderlistmodel/plugin.cpp | 2 | ||||
-rw-r--r-- | src/imports/folderlistmodel/qquickfolderlistmodel.cpp | 6 | ||||
-rw-r--r-- | src/imports/layouts/qquicklayout.cpp | 32 | ||||
-rw-r--r-- | src/imports/layouts/qquicklayout_p.h | 4 | ||||
-rw-r--r-- | src/imports/layouts/qquicklinearlayout.cpp | 54 | ||||
-rw-r--r-- | src/imports/layouts/qquicklinearlayout_p.h | 9 | ||||
-rw-r--r-- | src/imports/localstorage/plugin.cpp | 6 | ||||
-rw-r--r-- | src/imports/testlib/SignalSpy.qml | 10 | ||||
-rw-r--r-- | src/imports/testlib/TestCase.qml | 269 | ||||
-rw-r--r-- | src/imports/testlib/main.cpp | 1 | ||||
-rw-r--r-- | src/imports/testlib/qmldir | 1 | ||||
-rw-r--r-- | src/imports/testlib/toucheventsequence.qdoc | 110 | ||||
-rw-r--r-- | src/imports/window/plugins.qmltypes | 8 |
15 files changed, 459 insertions, 73 deletions
diff --git a/src/imports/folderlistmodel/fileinfothread.cpp b/src/imports/folderlistmodel/fileinfothread.cpp index 0b62935f87..d3e256bb7e 100644 --- a/src/imports/folderlistmodel/fileinfothread.cpp +++ b/src/imports/folderlistmodel/fileinfothread.cpp @@ -46,7 +46,7 @@ FileInfoThread::FileInfoThread(QObject *parent) : QThread(parent), abort(false), -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) watcher(0), #endif sortFlags(QDir::Name), @@ -61,11 +61,11 @@ FileInfoThread::FileInfoThread(QObject *parent) showOnlyReadable(false), caseSensitive(true) { -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) watcher = new QFileSystemWatcher(this); connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString))); connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateFile(QString))); -#endif // !QT_NO_FILESYSTEMWATCHER +#endif // filesystemwatcher } FileInfoThread::~FileInfoThread() @@ -80,7 +80,7 @@ FileInfoThread::~FileInfoThread() void FileInfoThread::clear() { QMutexLocker locker(&mutex); -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) watcher->removePaths(watcher->files()); watcher->removePaths(watcher->directories()); #endif @@ -89,7 +89,7 @@ void FileInfoThread::clear() void FileInfoThread::removePath(const QString &path) { QMutexLocker locker(&mutex); -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) if (!path.startsWith(QLatin1Char(':'))) watcher->removePath(path); #else @@ -103,7 +103,7 @@ void FileInfoThread::setPath(const QString &path) Q_ASSERT(!path.isEmpty()); QMutexLocker locker(&mutex); -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) if (!path.startsWith(QLatin1Char(':'))) watcher->addPath(path); #endif @@ -120,7 +120,7 @@ void FileInfoThread::setRootPath(const QString &path) rootPath = path; } -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) void FileInfoThread::dirChanged(const QString &directoryPath) { Q_UNUSED(directoryPath); @@ -204,7 +204,7 @@ void FileInfoThread::setCaseSensitive(bool on) condition.wakeAll(); } -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) void FileInfoThread::updateFile(const QString &path) { Q_UNUSED(path); diff --git a/src/imports/folderlistmodel/fileinfothread_p.h b/src/imports/folderlistmodel/fileinfothread_p.h index 56058fd6be..b505ece750 100644 --- a/src/imports/folderlistmodel/fileinfothread_p.h +++ b/src/imports/folderlistmodel/fileinfothread_p.h @@ -88,7 +88,7 @@ public: void setCaseSensitive(bool on); public Q_SLOTS: -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) void dirChanged(const QString &directoryPath); void updateFile(const QString &path); #endif @@ -103,7 +103,7 @@ private: QWaitCondition condition; volatile bool abort; -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) QFileSystemWatcher *watcher; #endif QList<FileProperty> currentFileList; diff --git a/src/imports/folderlistmodel/plugin.cpp b/src/imports/folderlistmodel/plugin.cpp index affde1c3aa..ef719109bd 100644 --- a/src/imports/folderlistmodel/plugin.cpp +++ b/src/imports/folderlistmodel/plugin.cpp @@ -62,12 +62,10 @@ public: void registerTypes(const char *uri) Q_DECL_OVERRIDE { Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.folderlistmodel")); -#ifndef QT_NO_DIRMODEL qmlRegisterType<QQuickFolderListModel>(uri,1,0,"FolderListModel"); qmlRegisterType<QQuickFolderListModel>(uri,2,0,"FolderListModel"); qmlRegisterType<QQuickFolderListModel,1>(uri,2,1,"FolderListModel"); qmlRegisterType<QQuickFolderListModel,2>(uri,2,2,"FolderListModel"); -#endif } }; //![class decl] diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 1c94fddecf..fdcce9c685 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -447,9 +447,9 @@ void QQuickFolderListModel::setFolder(const QUrl &folder) /*! \qmlproperty url FolderListModel::rootFolder - When the rootFolder is set, then this folder will - be threated as the root in the file system, so that - you can only travers sub folders from this rootFolder. + When this property is set, the given folder will + be treated as the root in the file system, so that + you can only traverse subfolders within it. */ QUrl QQuickFolderListModel::rootFolder() const { diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp index 07ada75a5f..3786d21727 100644 --- a/src/imports/layouts/qquicklayout.cpp +++ b/src/imports/layouts/qquicklayout.cpp @@ -763,18 +763,14 @@ void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value) { if (change == ItemChildAddedChange) { QQuickItem *item = value.item; - qmlobject_connect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); - qmlobject_connect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); - QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder); + QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility); if (isReady()) updateLayoutItems(); } else if (change == ItemChildRemovedChange) { QQuickItem *item = value.item; - qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); - qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickLayout, SLOT(invalidateSenderItem())); qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder); + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility); if (isReady()) updateLayoutItems(); } @@ -812,6 +808,30 @@ void QQuickLayout::itemSiblingOrderChanged(QQuickItem *item) updateLayoutItems(); } +void QQuickLayout::itemImplicitWidthChanged(QQuickItem *item) +{ + if (!isReady() || item->signalsBlocked()) + return; + invalidate(item); +} + +void QQuickLayout::itemImplicitHeightChanged(QQuickItem *item) +{ + if (!isReady() || item->signalsBlocked()) + return; + invalidate(item); +} + +void QQuickLayout::itemDestroyed(QQuickItem *item) +{ + Q_UNUSED(item); +} + +void QQuickLayout::itemVisibilityChanged(QQuickItem *item) +{ + Q_UNUSED(item); +} + void QQuickLayout::rearrange(const QSizeF &/*size*/) { m_dirty = false; diff --git a/src/imports/layouts/qquicklayout_p.h b/src/imports/layouts/qquicklayout_p.h index c7f04c1fed..eece6f8658 100644 --- a/src/imports/layouts/qquicklayout_p.h +++ b/src/imports/layouts/qquicklayout_p.h @@ -99,6 +99,10 @@ public: /* QQuickItemChangeListener */ void itemSiblingOrderChanged(QQuickItem *item) Q_DECL_OVERRIDE; + void itemImplicitWidthChanged(QQuickItem *item) Q_DECL_OVERRIDE; + void itemImplicitHeightChanged(QQuickItem *item) Q_DECL_OVERRIDE; + void itemDestroyed(QQuickItem *item) Q_DECL_OVERRIDE; + void itemVisibilityChanged(QQuickItem *item) Q_DECL_OVERRIDE; protected: void updatePolish() Q_DECL_OVERRIDE; diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp index 7fad395a29..13fdd496c2 100644 --- a/src/imports/layouts/qquicklinearlayout.cpp +++ b/src/imports/layouts/qquicklinearlayout.cpp @@ -303,17 +303,13 @@ QQuickGridLayoutBase::~QQuickGridLayoutBase() { Q_D(QQuickGridLayoutBase); - /* Avoid messy deconstruction, should give: - * Faster deconstruction - * Less risk of signals reaching already deleted objects - */ + // Remove item listeners so we do not act on signalling unnecessarily + // (there is no point, as the layout will be torn down anyway). for (int i = 0; i < itemCount(); ++i) { QQuickItem *item = itemAt(i); - qmlobject_disconnect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed())); - qmlobject_disconnect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged())); - qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickGridLayoutBase, SLOT(invalidateSenderItem())); - qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickGridLayoutBase, SLOT(invalidateSenderItem())); + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility); } + delete d->styleInfo; } @@ -436,23 +432,6 @@ int QQuickGridLayoutBase::itemCount() const return d->engine.itemCount(); } -void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &value) -{ - if (change == ItemChildAddedChange) { - quickLayoutDebug() << "ItemChildAddedChange"; - QQuickItem *item = value.item; - qmlobject_connect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed())); - qmlobject_connect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged())); - } else if (change == ItemChildRemovedChange) { - quickLayoutDebug() << "ItemChildRemovedChange"; - QQuickItem *item = value.item; - qmlobject_disconnect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed())); - qmlobject_disconnect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged())); - } - - QQuickLayout::itemChange(change, value); -} - void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem) { Q_D(QQuickGridLayoutBase); @@ -461,28 +440,29 @@ void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem) d->engine.removeRows(index, 1, d->orientation); } -void QQuickGridLayoutBase::onItemVisibleChanged() -{ - if (!isReady()) - return; - quickLayoutDebug() << "QQuickGridLayoutBase::onItemVisibleChanged"; - updateLayoutItems(); -} - -void QQuickGridLayoutBase::onItemDestroyed() +void QQuickGridLayoutBase::itemDestroyed(QQuickItem *item) { if (!isReady()) return; Q_D(QQuickGridLayoutBase); - quickLayoutDebug() << "QQuickGridLayoutBase::onItemDestroyed"; - QQuickItem *inDestruction = static_cast<QQuickItem *>(sender()); - if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(inDestruction)) { + quickLayoutDebug() << "QQuickGridLayoutBase::itemDestroyed"; + if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) { removeGridItem(gridItem); delete gridItem; invalidate(); } } +void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item) +{ + Q_UNUSED(item); + + if (!isReady()) + return; + quickLayoutDebug() << "QQuickGridLayoutBase::itemVisibilityChanged"; + updateLayoutItems(); +} + void QQuickGridLayoutBase::rearrange(const QSizeF &size) { Q_D(QQuickGridLayoutBase); diff --git a/src/imports/layouts/qquicklinearlayout_p.h b/src/imports/layouts/qquicklinearlayout_p.h index ca8b867824..b425df0fa4 100644 --- a/src/imports/layouts/qquicklinearlayout_p.h +++ b/src/imports/layouts/qquicklinearlayout_p.h @@ -77,6 +77,10 @@ public: Qt::LayoutDirection effectiveLayoutDirection() const; void setAlignment(QQuickItem *item, Qt::Alignment align) Q_DECL_OVERRIDE; + /* QQuickItemChangeListener */ + void itemDestroyed(QQuickItem *item) Q_DECL_OVERRIDE; + void itemVisibilityChanged(QQuickItem *item) Q_DECL_OVERRIDE; + protected: void updateLayoutItems() Q_DECL_OVERRIDE; QQuickItem *itemAt(int index) const Q_DECL_OVERRIDE; @@ -84,15 +88,10 @@ protected: void rearrange(const QSizeF &size) Q_DECL_OVERRIDE; virtual void insertLayoutItems() {} - void itemChange(ItemChange change, const ItemChangeData &data) Q_DECL_OVERRIDE; signals: Q_REVISION(1) void layoutDirectionChanged(); -protected slots: - void onItemVisibleChanged(); - void onItemDestroyed(); - private: void removeGridItem(QGridLayoutItem *gridItem); Q_DECLARE_PRIVATE(QQuickGridLayoutBase) diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index b8fd307215..ebffb07346 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -449,7 +449,7 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx) if (ok) { *w->d()->version = to_version; -#ifndef QT_NO_SETTINGS +#if QT_CONFIG(settings) QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(), scope.engine) + QLatin1String(".ini"), QSettings::IniFormat); ini.setValue(QLatin1String("Version"), to_version); #endif @@ -700,7 +700,7 @@ public: void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) { -#ifndef QT_NO_SETTINGS +#if QT_CONFIG(settings) QV4::Scope scope(args->v4engine()); if (scope.engine->qmlEngine()->offlineStoragePath().isEmpty()) V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, QQmlEngine::tr("SQL: can't create database, offline storage is disabled.")); @@ -770,7 +770,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) } args->setReturnValue(db.asReturnedValue()); -#endif // QT_NO_SETTINGS +#endif // settings } static QObject *module_api_factory(QQmlEngine *engine, QJSEngine *scriptEngine) diff --git a/src/imports/testlib/SignalSpy.qml b/src/imports/testlib/SignalSpy.qml index 200fc725f7..8a8e844a21 100644 --- a/src/imports/testlib/SignalSpy.qml +++ b/src/imports/testlib/SignalSpy.qml @@ -230,8 +230,14 @@ Item { qtest_prevSignalName = "" } if (target != null && signalName != "") { - var handlerName = qtest_signalHandlerName(signalName) - var func = target[handlerName] + // Look for the signal name in the object + var func = target[signalName] + if (typeof func !== "function") { + // If it is not a function, try looking for signal handler + // i.e. (onSignal) this is needed for cases where there is a property + // and a signal with the same name, e.g. Mousearea.pressed + func = target[qtest_signalHandlerName(signalName)] + } if (func === undefined) { spy.qtest_valid = false console.log("Signal '" + signalName + "' not found") diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 683200a259..ad7533c550 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -206,6 +206,59 @@ import Qt.test.qtestroot 1.0 will fail. Use the \l when and windowShown properties to track when the main window has been shown. + \section1 Managing Dynamically Created Test Objects + + A typical pattern with QML tests is to + \l {Dynamic QML Object Creation from JavaScript}{dynamically create} + an item and then destroy it at the end of the test function: + + \code + TestCase { + id: testCase + name: "MyTest" + when: windowShown + + function test_click() { + var item = Qt.createQmlObject("import QtQuick 2.0; Item {}", testCase); + verify(item); + + // Test item... + + item.destroy(); + } + } + \endcode + + The problem with this pattern is that any failures in the test function + will cause the call to \c item.destroy() to be skipped, leaving the item + hanging around in the scene until the test case has finished. This can + result in interference with future tests; for example, by blocking input + events or producing unrelated debug output that makes it difficult to + follow the code's execution. + + By calling \l createTemporaryQmlObject() instead, the object is guaranteed + to be destroyed at the end of the test function: + + \code + TestCase { + id: testCase + name: "MyTest" + when: windowShown + + function test_click() { + var item = createTemporaryQmlObject("import QtQuick 2.0; Item {}", testCase); + verify(item); + + // Test item... + + // Don't need to worry about destroying "item" here. + } + } + \endcode + + For objects that are created via the \l {Component::}{createObject()} function + of \l Component, the \l createTemporaryObject() function can be used. + \sa {QtTest::SignalSpy}{SignalSpy}, {Qt Quick Test Reference Documentation} */ @@ -221,7 +274,7 @@ Item { \qmlproperty string TestCase::name This property defines the name of the test case for result reporting. - The default is the empty string. + The default value is an empty string. \code TestCase { @@ -358,6 +411,8 @@ Item { /*! \internal */ property var qtest_events: qtest_events_normal TestEvent { id: qtest_events_normal } + /*! \internal */ + property var qtest_temporaryObjects: [] /*! \qmlmethod TestCase::fail(message = "") @@ -461,6 +516,110 @@ Item { throw new Error("QtQuickTest::fail") } + /*! + \since 5.9 + \qmlmethod object TestCase::createTemporaryQmlObject(string qml, object parent, string filePath) + + This function dynamically creates a QML object from the given \a qml + string with the specified \a parent. The returned object will be + destroyed (if it was not already) after \l cleanup() has finished + executing, meaning that objects created with this function are + guaranteed to be destroyed after each test, regardless of whether or + not the tests fail. + + If there was an error while creating the object, \c null will be + returned. + + If \a filePath is specified, it will be used for error reporting for + the created object. + + This function calls + \l {QtQml::Qt::createQmlObject()}{Qt.createQmlObject()} internally. + + \sa {Managing Dynamically Created Test Objects} + */ + function createTemporaryQmlObject(qml, parent, filePath) { + if (typeof qml !== "string") { + qtest_results.fail("First argument must be a string of QML; actual type is " + typeof qml, + util.callerFile(), util.callerLine()); + throw new Error("QtQuickTest::fail"); + } + + if (!parent || typeof parent !== "object") { + qtest_results.fail("Second argument must be a valid parent object; actual type is " + typeof parent, + util.callerFile(), util.callerLine()); + throw new Error("QtQuickTest::fail"); + } + + if (filePath !== undefined && typeof filePath !== "string") { + qtest_results.fail("Third argument must be a file path string; actual type is " + typeof filePath, + util.callerFile(), util.callerLine()); + throw new Error("QtQuickTest::fail"); + } + + var object = Qt.createQmlObject(qml, parent, filePath); + qtest_temporaryObjects.push(object); + return object; + } + + /*! + \since 5.9 + \qmlmethod object TestCase::createTemporaryObject(Component component, object parent, object properties) + + This function dynamically creates a QML object from the given + \a component with the specified \a parent and \a properties. + The returned object will be destroyed (if it was not already) after + \l cleanup() has finished executing, meaning that objects created with + this function are guaranteed to be destroyed after each test, + regardless of whether or not the tests fail. + + If there was an error while creating the object, \c null will be + returned. + + This function calls + \l {QtQml::Component::createObject()}{component.createObject()} + internally. + + \sa {Managing Dynamically Created Test Objects} + */ + function createTemporaryObject(component, parent, properties) { + if (typeof component !== "object") { + qtest_results.fail("First argument must be a Component; actual type is " + typeof component, + util.callerFile(), util.callerLine()); + throw new Error("QtQuickTest::fail"); + } + + if (!parent && parent !== null) { + qtest_results.fail("Second argument must be a parent object or null; actual type is " + typeof parent, + util.callerFile(), util.callerLine()); + throw new Error("QtQuickTest::fail"); + } + + if (properties && typeof properties !== "object") { + qtest_results.fail("Third argument must be an object; actual type is " + typeof properties, + util.callerFile(), util.callerLine()); + throw new Error("QtQuickTest::fail"); + } + + var object = component.createObject(parent, properties ? properties : ({})); + qtest_temporaryObjects.push(object); + return object; + } + + /*! + \internal + + Destroys all temporary objects that still exist. + */ + function qtest_destroyTemporaryObjects() { + for (var i = 0; i < qtest_temporaryObjects.length; ++i) { + var temporaryObject = qtest_temporaryObjects[i]; + if (temporaryObject) + temporaryObject.destroy(); + } + qtest_temporaryObjects = []; + } + /*! \internal */ // Determine what is o. // Discussions and reference: http://philrathe.com/articles/equiv @@ -835,7 +994,7 @@ Item { \c{QEXPECT_FAIL(tag, message, Abort)} in C++. If the test is not data-driven, then \a tag must be set to - the empty string. + an empty string. \sa expectFailContinue() */ @@ -861,7 +1020,7 @@ Item { \c{QEXPECT_FAIL(tag, message, Continue)} in C++. If the test is not data-driven, then \a tag must be set to - the empty string. + an empty string. \sa expectFail() */ @@ -1317,6 +1476,109 @@ Item { qtest_fail("window not shown", 2) } + /*! + \qmlmethod TouchEventSequence TestCase::touchEvent(object item) + + \since 5.9 + + Begins a sequence of touch events through a simulated QTouchDevice::TouchScreen. + Events are delivered to the window containing \a item. + + The returned object is used to enumerate events to be delivered through a single + QTouchEvent. Touches are delivered to the window containing the TestCase unless + otherwise specified. + + \code + Rectangle { + width: 640; height: 480 + + MultiPointTouchArea { + id: area + anchors.fill: parent + + property bool touched: false + + onPressed: touched = true + } + + TestCase { + name: "ItemTests" + when: area.pressed + id: test1 + + function test_touch() { + var touch = touchEvent(area); + touch.press(0, area, 10, 10); + touch.commit(); + verify(area.touched); + } + } + } + \endcode + + \sa TouchEventSequence::press(), TouchEventSequence::move(), TouchEventSequence::release(), TouchEventSequence::stationary(), TouchEventSequence::commit(), QTouchDevice::TouchScreen + */ + + function touchEvent(item) { + if (!item) + qtest_fail("No item given to touchEvent", 1) + + return { + _defaultItem: item, + _sequence: qtest_events.touchEvent(item), + + press: function (id, target, x, y) { + if (!target) + target = this._defaultItem; + if (id === undefined) + qtest_fail("No id given to TouchEventSequence::press", 1); + if (x === undefined) + x = target.width / 2; + if (y === undefined) + y = target.height / 2; + this._sequence.press(id, target, x, y); + return this; + }, + + move: function (id, target, x, y) { + if (!target) + target = this._defaultItem; + if (id === undefined) + qtest_fail("No id given to TouchEventSequence::move", 1); + if (x === undefined) + x = target.width / 2; + if (y === undefined) + y = target.height / 2; + this._sequence.move(id, target, x, y); + return this; + }, + + stationary: function (id) { + if (id === undefined) + qtest_fail("No id given to TouchEventSequence::stationary", 1); + this._sequence.stationary(id); + return this; + }, + + release: function (id, target, x, y) { + if (!target) + target = this._defaultItem; + if (id === undefined) + qtest_fail("No id given to TouchEventSequence::release", 1); + if (x === undefined) + x = target.width / 2; + if (y === undefined) + y = target.height / 2; + this._sequence.release(id, target, x, y); + return this; + }, + + commit: function () { + this._sequence.commit(); + return this; + } + }; + } // Functions that can be overridden in subclasses for init/cleanup duties. /*! @@ -1389,6 +1651,7 @@ Item { qtest_runInternal(prop, arg) qtest_results.finishTestData() qtest_runInternal("cleanup") + qtest_destroyTemporaryObjects() qtest_results.finishTestDataCleanup() // wait(0) will call processEvents() so objects marked for deletion // in the test function will be deleted. diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index 4e2bd919e9..3c28000e35 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -158,6 +158,7 @@ public: qmlRegisterType<QuickTestResult, 1>(uri,1,1,"TestResult"); qmlRegisterType<QuickTestEvent>(uri,1,0,"TestEvent"); qmlRegisterType<QuickTestUtil>(uri,1,0,"TestUtil"); + qmlRegisterType<QQuickTouchEventSequence>(); } }; diff --git a/src/imports/testlib/qmldir b/src/imports/testlib/qmldir index 9da8ebb4be..e5757f6a88 100644 --- a/src/imports/testlib/qmldir +++ b/src/imports/testlib/qmldir @@ -3,4 +3,5 @@ plugin qmltestplugin classname QTestQmlModule typeinfo plugins.qmltypes TestCase 1.0 TestCase.qml +TestCase 1.2 TestCase.qml SignalSpy 1.0 SignalSpy.qml diff --git a/src/imports/testlib/toucheventsequence.qdoc b/src/imports/testlib/toucheventsequence.qdoc new file mode 100644 index 0000000000..f85a1cd4f9 --- /dev/null +++ b/src/imports/testlib/toucheventsequence.qdoc @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jeremy Katz +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype TouchEventSequence + \inqmlmodule QtTest + \ingroup qtquicktest + \brief TouchEventSequence is used to build and dispatch touch events + for testing. + + \since 5.9 + + A TouchEventSequence is created by calling \l [QML] {TestCase::touchEvent()}{TestCase.touchEvent()}. + The type can not be directly instantiated. Each method provided by the type returns + the same object, allowing chained calls. + + For example: + \code + touchEvent(item).press(0).commit(); + \endcode + is equivalent to: + \code + var sequence = touchEvent(item); + sequence.press(0); + sequence.commit(); + \endcode + + Events are delivered to the window which contains the item specified in touchEvent. + + \sa TestCase::touchEvent(), QTest::QTouchEventSequence +*/ + +/*! + \qmlmethod TouchEventSequence TouchEventSequence::press(int touchId, object item, real x = item.width / 2, real y = item.height / 2) + + Creates a new point identified as \a touchId, at the point indicated by \a x and \a y relative to \a item. + Further use of the same touch point should maintain the same touchId. + + Item defaults to the value provided via touchEvent(). + X and y default to the midpoint of the item. +*/ + +/*! + \qmlmethod TouchEventSequence TouchEventSequence::move(int touchId, object item, real x = item.width / 2, real y = item.height / 2) + + Moves \a touchId to the point indicated by \a x and \a y relative to \a item. + + Item defaults to the value provided via touchEvent(). + X and y default to the midpoint of the item. +*/ + +/*! + \qmlmethod TouchEventSequence TouchEventSequence::release(int touchId, object item, real x = item.width / 2, real y = item.height / 2) + + Removes \a touchId at the point indicated by \a x and \a y relative to \a item. + + Item defaults to the value provided via touchEvent(). + X and y default to the midpoint of the item. +*/ + +/*! + \qmlmethod TouchEventSequence TouchEventSequence::stationary(int touchId) + + Indicates that \a touchId is present but otherwise unchanged from prior events. +*/ + +/*! + \qmlmethod TouchEventSequence TouchEventSequence::commit() + + Sends the touch event composed by prior use of press(), move(), release(), and stationary(). + Following commit's return, the TouchEventSequence can be used to compose a new event. + + \code + var sequence = touchEvent(target); + // Touch the middle of target with 1 point + sequence.press(1); + sequence.commit(); + + // Begin a new event + // Move the point to target's upper left corner + sequence.move(1, target, 0, 0); + sequence.commit(); + \endcode + + Commit is automatically invoked when the TouchEventSequence object is destroyed. +*/ diff --git a/src/imports/window/plugins.qmltypes b/src/imports/window/plugins.qmltypes index 6a8dbfa024..a79bd8c332 100644 --- a/src/imports/window/plugins.qmltypes +++ b/src/imports/window/plugins.qmltypes @@ -55,8 +55,12 @@ Module { name: "QQuickWindow" defaultProperty: "data" prototype: "QWindow" - exports: ["QtQuick.Window/Window 2.0"] - exportMetaObjectRevisions: [0] + exports: [ + "QtQuick.Window/Window 2.0", + "QtQuick.Window/Window 2.1", + "QtQuick.Window/Window 2.2" + ] + exportMetaObjectRevisions: [0, 1, 2] Enum { name: "SceneGraphError" values: { |