diff options
Diffstat (limited to 'tests')
214 files changed, 10351 insertions, 180 deletions
diff --git a/tests/auto/qml/bindingdependencyapi/bindingdependencyapi.pro b/tests/auto/qml/bindingdependencyapi/bindingdependencyapi.pro new file mode 100644 index 0000000000..430d87ab5b --- /dev/null +++ b/tests/auto/qml/bindingdependencyapi/bindingdependencyapi.pro @@ -0,0 +1,11 @@ +CONFIG += testcase +TARGET = tst_bindingdependencyapi +macos:CONFIG -= app_bundle + +SOURCES += tst_bindingdependencyapi.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp new file mode 100644 index 0000000000..6f24f85f6d --- /dev/null +++ b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <private/qqmldata_p.h> +#include <private/qqmlbinding_p.h> +#include <QtQuick/private/qquicktext_p.h> +#include <QtQuick/private/qquickrectangle_p.h> +#include "../../shared/util.h" +#include <qqmlcontext.h> + +class tst_bindingdependencyapi : public QObject +{ + Q_OBJECT +public: + tst_bindingdependencyapi(); + +private slots: + void testSingleDep_data(); + void testSingleDep(); + void testManyDeps_data(); + void testManyDeps(); + void testConditionalDependencies_data(); + void testConditionalDependencies(); + void testBindingLoop(); + +private: + bool findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value); +}; + +tst_bindingdependencyapi::tst_bindingdependencyapi() +{ +} + + +void tst_bindingdependencyapi::testSingleDep_data() +{ + QTest::addColumn<QByteArray>("code"); + QTest::addColumn<QString>("referencedObjectName"); + + QTest::addRow("context property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { text: labelText }\n" + "}") << "rect"; + + QTest::addRow("scope property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: \"I am wrong!\"\n" + "Text {\n" + "objectName: \"text\"\n" + "property string labelText: \"Hello world!\"\n" + "text: labelText\n" + "}\n" + "}") << "text"; + + QTest::addRow("id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { text: rect.labelText }\n" + "}") << "rect"; + + QTest::addRow("dynamic context property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return labelText; }); }\n" + "}") << "rect"; + + QTest::addRow("dynamic scope property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: \"I am wrong!\"\n" + "Text {\n" + "objectName: \"text\"\n" + "property string labelText: \"Hello world!\"\n" + "Component.onCompleted: text = Qt.binding(function() { return labelText; });\n" + "}\n" + "}") << "text"; + + QTest::addRow("dynamic id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return rect.labelText; }); }\n" + "}") << "rect"; +} + +void tst_bindingdependencyapi::testSingleDep() +{ + QFETCH(QByteArray, code); + QFETCH(QString, referencedObjectName); + + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(code, QUrl()); + QObject *rect = c.create(); + QTest::qWait(10); + QVERIFY(rect != 0); + QObject *text = rect->findChildren<QQuickText *>().front(); + + QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + auto dependency = dependencies.front(); + QVERIFY(dependency.isValid()); + QCOMPARE(quintptr(dependency.object()), quintptr(referencedObject)); + QCOMPARE(dependency.property().name(), "labelText"); + QCOMPARE(dependency.read().toString(), QStringLiteral("Hello world!")); + QCOMPARE(dependency, QQmlProperty(referencedObject, "labelText")); + + delete rect; +} + +bool tst_bindingdependencyapi::findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value) +{ + auto dep = std::find_if(properties.cbegin(), properties.cend(), [&](const QQmlProperty &dep) { + return dep.object() == obj + && dep.property().name() == propertyName + && dep.read() == value; + }); + if (dep == properties.cend()) { + qWarning() << "Searched for property with:" << "{ object:" << obj << ", propertyName:" << propertyName << ", value:" << value << "}" << "but only found:"; + for (auto dep : properties) { + qWarning() << "{ object:" << dep.object() << ", propertyName:" << dep.property().name() << ", value:" << dep.read() << "}"; + } + return false; + } + return true; +} + +void tst_bindingdependencyapi::testManyDeps_data() +{ + QTest::addColumn<QByteArray>("code"); + + QTest::addRow("permanent binding") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: 'rect'\n" + "property string name: 'world'\n" + "Text {\n" + "text: config.helloWorldTemplate.arg(greeting).arg(rect.name) \n" + "property string greeting: 'Hello'\n" + "}\n" + "QtObject { id: config; objectName: 'config'; property string helloWorldTemplate: '%1 %2!' }\n" + "}"); + + QTest::addRow("dynamic binding") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: 'rect'\n" + "property string name: 'world'\n" + "Text {\n" + "Component.onCompleted: text = Qt.binding(function() { return config.helloWorldTemplate.arg(greeting).arg(rect.name); }); \n" + "property string greeting: 'Hello'\n" + "}\n" + "QtObject { id: config; objectName: 'config'; property string helloWorldTemplate: '%1 %2!' }\n" + "}"); +} + +void tst_bindingdependencyapi::testManyDeps() +{ + QFETCH(QByteArray, code); + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(code, QUrl()); + QObject *rect = c.create(); + if (c.isError()) { + qWarning() << c.errorString(); + } + QTest::qWait(100); + QVERIFY(rect != 0); + QObject *text = rect->findChildren<QQuickText *>().front(); + QObject *configObj = rect->findChild<QObject *>("config"); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 3); + + QVERIFY(findProperties(dependencies, rect, "name", "world")); + QVERIFY(findProperties(dependencies, text, "greeting", "Hello")); + QVERIFY(findProperties(dependencies, configObj, "helloWorldTemplate", "%1 %2!")); + + delete rect; +} + +void tst_bindingdependencyapi::testConditionalDependencies_data() +{ + QTest::addColumn<QByteArray>("code"); + QTest::addColumn<QString>("referencedObjectName"); + + QTest::addRow("id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Text { text: rect.haveDep ? rect.labelText : '' }\n" + "}") << "rect"; + + QTest::addRow("dynamic context property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "objectName: \"rect\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return haveDep ? labelText : ''; }); }\n" + "}") << "rect"; + + QTest::addRow("dynamic scope property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: \"I am wrong!\"\n" + "Text {\n" + "objectName: \"text\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Component.onCompleted: text = Qt.binding(function() { return haveDep ? labelText : ''; });\n" + "}\n" + "}") << "text"; + + QTest::addRow("dynamic id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return rect.haveDep ? rect.labelText : ''; }); }\n" + "}") << "rect"; +} + +void tst_bindingdependencyapi::testConditionalDependencies() +{ + QFETCH(QByteArray, code); + QFETCH(QString, referencedObjectName); + + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(code, QUrl()); + QObject *rect = c.create(); + QTest::qWait(10); + QVERIFY(rect != 0); + QObject *text = rect->findChildren<QQuickText *>().front(); + + QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false)); + + referencedObject->setProperty("haveDep", true); + dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 2); + QVERIFY(findProperties(dependencies, referencedObject, "haveDep", true)); + QVERIFY(findProperties(dependencies, referencedObject, "labelText", "Hello world!")); + + referencedObject->setProperty("haveDep", false); + dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false)); + + delete rect; +} + +void tst_bindingdependencyapi::testBindingLoop() +{ + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: label.text\n" + "Text {\n" + "id: label\n" + "text: labelText\n" + "}\n" + "}"), QUrl()); + QObject *rect = c.create(); + if (c.isError()) { + qWarning() << c.errorString(); + } + QTest::qWait(100); + QVERIFY(rect != 0); + QObject *text = rect->findChildren<QQuickText *>().front(); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + auto dependency = dependencies.front(); + QVERIFY(dependency.isValid()); + QCOMPARE(quintptr(dependency.object()), quintptr(rect)); + QCOMPARE(dependency.property().name(), "labelText"); + + delete rect; +} + +QTEST_MAIN(tst_bindingdependencyapi) + +#include "tst_bindingdependencyapi.moc" diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp index d248cf9708..c297b5cab6 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp @@ -865,6 +865,8 @@ void tst_QQmlDebugJS::connect() QFETCH(bool, restrictMode); QFETCH(bool, qmlscene); init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode); + if (QTest::currentTestFailed()) + return; client->connect(); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); } @@ -877,6 +879,8 @@ void tst_QQmlDebugJS::interrupt() QFETCH(bool, redundantRefs); QFETCH(bool, namesAsObjects); init(qmlscene); + if (QTest::currentTestFailed()) + return; client->connect(redundantRefs, namesAsObjects); client->interrupt(); @@ -891,6 +895,8 @@ void tst_QQmlDebugJS::getVersion() QFETCH(bool, redundantRefs); QFETCH(bool, namesAsObjects); init(qmlscene); + if (QTest::currentTestFailed()) + return; client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); @@ -906,6 +912,8 @@ void tst_QQmlDebugJS::getVersionWhenAttaching() QFETCH(bool, namesAsObjects); init(qmlscene, QLatin1String(TIMER_QMLFILE), false); + if (QTest::currentTestFailed()) + return; client->connect(redundantRefs, namesAsObjects); client->version(); @@ -920,6 +928,8 @@ void tst_QQmlDebugJS::disconnect() QFETCH(bool, redundantRefs); QFETCH(bool, namesAsObjects); init(qmlscene); + if (QTest::currentTestFailed()) + return; client->connect(redundantRefs, namesAsObjects); client->disconnect(); @@ -935,6 +945,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -958,6 +970,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() int sourceLine = 34; init(qmlscene, CREATECOMPONENT_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -979,6 +993,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() QFETCH(bool, namesAsObjects); int sourceLine = 35; init(qmlscene, TIMER_QMLFILE); + if (QTest::currentTestFailed()) + return; client->connect(redundantRefs, namesAsObjects); //We can set the breakpoint after connect() here because the timer is repeating and if we miss @@ -1004,6 +1020,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() int sourceLine = 31; init(qmlscene, LOADJSFILE_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1028,6 +1046,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComment() int sourceLine = 34; int actualLine = 36; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1053,6 +1073,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() int sourceLine = 35; int actualLine = 36; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1077,6 +1099,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() int sourceLine = 39; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1099,6 +1123,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() int out = 10; int sourceLine = 37; init(qmlscene, CONDITION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->connect(redundantRefs, namesAsObjects); //The breakpoint is in a timer loop so we can set it after connect(). @@ -1136,6 +1162,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() QFETCH(bool, redundantRefs); QFETCH(bool, namesAsObjects); init(qmlscene, QUIT_QMLFILE); + if (QTest::currentTestFailed()) + return; int sourceLine = 36; @@ -1160,6 +1188,8 @@ void tst_QQmlDebugJS::setBreakpointWhenAttaching() { int sourceLine = 35; init(true, QLatin1String(TIMER_QMLFILE), false); + if (QTest::currentTestFailed()) + return; client->connect(); @@ -1183,6 +1213,8 @@ void tst_QQmlDebugJS::clearBreakpoint() int sourceLine1 = 37; int sourceLine2 = 38; init(qmlscene, CHANGEBREAKPOINT_QMLFILE); + if (QTest::currentTestFailed()) + return; client->connect(redundantRefs, namesAsObjects); //The breakpoints are in a timer loop so we can set them after connect(). @@ -1231,6 +1263,8 @@ void tst_QQmlDebugJS::setExceptionBreak() QFETCH(bool, namesAsObjects); init(qmlscene, EXCEPTION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setExceptionBreak(QJSDebugClient::All,true); client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); @@ -1245,6 +1279,8 @@ void tst_QQmlDebugJS::stepNext() int sourceLine = 37; init(qmlscene, STEPACTION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1272,6 +1308,8 @@ void tst_QQmlDebugJS::stepIn() int sourceLine = 41; int actualLine = 37; init(qmlscene, STEPACTION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); client->connect(redundantRefs, namesAsObjects); @@ -1299,6 +1337,8 @@ void tst_QQmlDebugJS::stepOut() int sourceLine = 37; int actualLine = 41; init(qmlscene, STEPACTION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1326,6 +1366,8 @@ void tst_QQmlDebugJS::continueDebugging() int sourceLine1 = 41; int sourceLine2 = 38; init(qmlscene, STEPACTION_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); @@ -1353,6 +1395,8 @@ void tst_QQmlDebugJS::backtrace() int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1371,6 +1415,8 @@ void tst_QQmlDebugJS::getFrameDetails() int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1389,6 +1435,8 @@ void tst_QQmlDebugJS::getScopeDetails() int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1402,6 +1450,8 @@ void tst_QQmlDebugJS::evaluateInGlobalScope() { //void evaluate(QString expr, int frame = -1); init(true); + if (QTest::currentTestFailed()) + return; client->connect(); @@ -1426,6 +1476,8 @@ void tst_QQmlDebugJS::evaluateInLocalScope() QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); client->connect(redundantRefs, namesAsObjects); @@ -1514,6 +1566,8 @@ void tst_QQmlDebugJS::getScripts() QFETCH(bool, redundantRefs); QFETCH(bool, namesAsObjects); init(qmlscene); + if (QTest::currentTestFailed()) + return; client->setBreakpoint(QString(TEST_QMLFILE), 35, -1, true); client->connect(redundantRefs, namesAsObjects); diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/BLACKLIST b/tests/auto/qml/debugger/qqmlprofilerservice/BLACKLIST new file mode 100644 index 0000000000..5fb1dc193b --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/BLACKLIST @@ -0,0 +1,2 @@ +# QTQAINFRA-1334 +windows gcc diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index b8e1f1f21d..e5eb1c428f 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -327,9 +327,12 @@ void tst_QQmlProfilerService::connect(bool block, const QString &testFile, bool .arg(restrictServices ? QStringLiteral(",services:CanvasFrameRate") : QString()) << QQmlDataTest::instance()->testFile(testFile); - m_process = new QQmlDebugProcess(executable, this); - m_process->start(QStringList() << arguments); - QVERIFY2(m_process->waitForSessionStart(), "Could not launch application, or did not get 'Waiting for connection'."); + QScopedPointer<QQmlDebugProcess> process; + process.reset(new QQmlDebugProcess(executable, this)); + process->start(QStringList() << arguments); + QVERIFY2(process->waitForSessionStart(), "Could not launch application, or did not get 'Waiting for connection'."); + + m_process = process.take(); m_connection = new QQmlDebugConnection(); m_client = new QQmlProfilerTestClient(m_connection); @@ -550,6 +553,8 @@ void tst_QQmlProfilerService::connect() QFETCH(bool, traceEnabled); connect(blockMode, "test.qml", restrictMode); + if (QTest::currentTestFailed() || QTestResult::skipCurrentTest()) + return; // if the engine is waiting, then the first message determines if it starts with trace enabled if (!traceEnabled) diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 441f8c113f..fd74135727 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -630,7 +630,7 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail_head.value("value").toString(), QStringLiteral("asdf")); QJsonObject b_tail_tail = b_tail_props.at(1).toObject(); QCOMPARE(b_tail_tail.value("name").toString(), QStringLiteral("tail")); - QCOMPARE(b_tail_tail.value("type").toString(), QStringLiteral("null")); + QCOMPARE(b_tail_tail.value("type").toString(), QStringLiteral("object")); QVERIFY(b_tail_tail.value("value").isNull()); } diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 49f107452a..27498de473 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -29,3 +29,150 @@ Sbp_12.5_A9_T3 failing Sbp_12.6.1_A13_T3 failing Sbp_12.6.2_A13_T3 failing Sbp_12.6.4_A13_T3 failing + +# es6: function length attributes are configurable, wasn't in es5 +S15.1.2.2_A9.2 failing +S15.1.3.1_A5.2 failing +S15.1.3.2_A5.2 failing +S15.1.3.3_A5.2 failing +S15.1.2.3_A7.2 failing +S15.1.2.4_A2.2 failing +S15.1.2.5_A2.2 failing +S15.1.3.4_A5.2 failing +15.2.3.3-4-186 failing +S15.2.4.2_A9 failing +S15.2.4.3_A9 failing +S15.2.4.4_A9 failing +S15.2.4.5_A9 failing +S15.2.4.6_A9 failing +S15.2.4.7_A9 failing +15.3.3.2-1 failing +15.4.4.2_A4.2 +S15.3.4.2_A9 failing +S15.3.4.3_A9 failing +S15.3.4.4_A9 failing +15.3.4.5-15-2 failing +S15.4.4.2_A4.2 failing +S15.4.4.3_A4.2 failing +S15.4.4.4_A4.2 failing +S15.4.4.5_A6.2 failing +S15.4.4.6_A5.2 failing +S15.4.4.7_A6.2 failing +S15.4.4.8_A5.2 failing +S15.4.4.9_A5.2 failing +S15.4.4.10_A5.2 failing +S15.4.4.11_A7.2 failing +S15.4.4.12_A5.2 failing +S15.4.4.13_A5.2 failing +S15.5.4.10_A9 failing +S15.5.4.11_A9 failing +S15.5.4.12_A9 failing +S15.5.4.13_A9 failing +S15.5.4.14_A9 failing +S15.5.4.15_A9 failing +S15.5.4.16_A9 failing +S15.5.4.17_A9 failing +S15.5.4.18_A9 failing +S15.5.4.19_A9 failing +S15.5.4.4_A9 failing +S15.5.4.5_A9 failing +S15.5.4.6_A9 failing +S15.5.4.7_A9 failing +S15.5.4.8_A9 failing +S15.5.4.9_A9 failing +S15.9.4.2_A3_T2 failing +S15.9.4.3_A3_T2 failing +S15.9.5.2_A3_T2 failing +S15.9.5.3_A3_T2 failing +S15.9.5.4_A3_T2 failing +S15.9.5.5_A3_T2 failing +S15.9.5.1_A3_T2 failing +S15.9.5.10_A3_T2 failing +S15.9.5.11_A3_T2 failing +S15.9.5.12_A3_T2 failing +S15.9.5.13_A3_T2 failing +S15.9.5.14_A3_T2 failing +S15.9.5.15_A3_T2 failing +S15.9.5.16_A3_T2 failing +S15.9.5.17_A3_T2 failing +S15.9.5.18_A3_T2 failing +S15.9.5.19_A3_T2 failing +S15.9.5.20_A3_T2 failing +S15.9.5.21_A3_T2 failing +S15.9.5.22_A3_T2 failing +S15.9.5.23_A3_T2 failing +S15.9.5.24_A3_T2 failing +S15.9.5.25_A3_T2 failing +S15.9.5.26_A3_T2 failing +S15.9.5.27_A3_T2 failing +S15.9.5.28_A3_T2 failing +S15.9.5.29_A3_T2 failing +S15.9.5.30_A3_T2 failing +S15.9.5.31_A3_T2 failing +S15.9.5.32_A3_T2 failing +S15.9.5.33_A3_T2 failing +S15.9.5.34_A3_T2 failing +S15.9.5.35_A3_T2 failing +S15.9.5.36_A3_T2 failing +S15.9.5.37_A3_T2 failing +S15.9.5.38_A3_T2 failing +S15.9.5.39_A3_T2 failing +S15.9.5.40_A3_T2 failing +S15.9.5.41_A3_T2 failing +S15.9.5.42_A3_T2 failing +S15.9.5.6_A3_T2 failing +S15.9.5.7_A3_T2 failing +S15.9.5.8_A3_T2 failing +S15.9.5.9_A3_T2 failing +S15.10.6.2_A9 failing +S15.10.6.3_A9 failing +S15.10.6.4_A9 failing + +# es6: Object.freeze(v) on a non-object returns v, no longer TypeError +15.2.3.9-1 failing +15.2.3.9-1-1 failing +15.2.3.9-1-2 failing +15.2.3.9-1-3 failing +15.2.3.9-1-4 failing +# es6: Object.preventExtensions(O) on a non-object, no longer TypeError +15.2.3.10-1 failing +15.2.3.10-1-3 failing +15.2.3.10-1-4 failing +# es6: Object.isSealed(O) on a non-object, no longer TypeError +15.2.3.11-1 +# es6: Object.isFrozen(O) on a non-object, no longer TypeError +15.2.3.12-1 +15.2.3.12-1-3 +15.2.3.12-1-4 +# es6: Object.isExtensible(O) on a non-object, no longer TypeError +15.2.3.13-1 +15.2.3.13-1-3 +15.2.3.13-1-4 +# es6: Object.keys(O) on a non-object, no longer TypeError +15.2.3.14-1-1 +15.2.3.14-1-2 +15.2.3.14-1-3 +15.2.3.14-1 +15.2.3.14-2 +15.2.3.14-3 +# es6: Object.getOwnPropertyDescriptor(O) on a non-object, no longer TypeError +15.2.3.3-1 +15.2.3.3-1-3 +15.2.3.3-1-4 +# es6: Object.getPrototypeOf(O) on a non-object, no longer TypeError +15.2.3.2-1 +15.2.3.2-1-3 +15.2.3.2-1-4 +# es6: Object.getOwnPropertyNames(O) on a non-object, no longer TypeError +15.2.3.4-1 +15.2.3.4-1-4 +15.2.3.4-1-5 +# es6: Object.seal(O) on a non-object, no longer TypeError +15.2.3.8-1 +15.2.3.8-1-1 +15.2.3.8-1-2 +15.2.3.8-1-3 +15.2.3.8-1-4 + +# es6: Date.prototype is no longer a DateObject +15.9.5.40_1 failing diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 2642d10545..a3a2efd565 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -568,10 +568,6 @@ void tst_QJSEngine::newDate() QCOMPARE(date.isDate(), true); QCOMPARE(date.isObject(), true); QVERIFY(!date.isCallable()); - // prototype should be Date.prototype - QVERIFY(!date.prototype().isUndefined()); - QCOMPARE(date.prototype().isDate(), true); - QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); } { @@ -580,10 +576,6 @@ void tst_QJSEngine::newDate() QVERIFY(!date.isUndefined()); QCOMPARE(date.isDate(), true); QCOMPARE(date.isObject(), true); - // prototype should be Date.prototype - QVERIFY(!date.prototype().isUndefined()); - QCOMPARE(date.prototype().isDate(), true); - QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); QCOMPARE(date.toDateTime(), dt); } @@ -1114,7 +1106,7 @@ void tst_QJSEngine::builtinFunctionNames_data() QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear"); QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear"); QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString"); - QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString"); + QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toUTCString"); // yes, this is per spec QTest::newRow("Error") << QString("Error") << QString("Error"); // QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace"); @@ -1192,6 +1184,7 @@ void tst_QJSEngine::builtinFunctionNames_data() QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf"); QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare"); QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match"); + QTest::newRow("String.prototype.repeat") << QString("String.prototype.repeat") << QString("repeat"); QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace"); QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search"); QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice"); diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 12a8bd3829..69af3cd13b 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -72,7 +72,8 @@ PRIVATETESTS += \ qqmlimport \ qqmlobjectmodel \ qv4mm \ - ecmascripttests + ecmascripttests \ + bindingdependencyapi } qtHaveModule(widgets) { diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index b7e616a050..1f80ff46d0 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -43,6 +43,7 @@ private slots: void loadGeneratedFile(); void translationExpressionSupport(); + void signalHandlerParameters(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -158,6 +159,41 @@ void tst_qmlcachegen::translationExpressionSupport() QCOMPARE(obj->property("text").toString(), QString("All Ok")); } +void tst_qmlcachegen::signalHandlerParameters() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.0\n" + "QtObject {\n" + " property real result: 0\n" + " signal testMe(real value);\n" + " onTestMe: result = value;\n" + " function runTest() { testMe(42); }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QMetaObject::invokeMethod(obj.data(), "runTest"); + QCOMPARE(obj->property("result").toInt(), 42); +} + QTEST_GUILESS_MAIN(tst_qmlcachegen) #include "tst_qmlcachegen.moc" diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp index 838966e2a0..68e11e3551 100644 --- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp +++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp @@ -105,12 +105,12 @@ void tst_qmlplugindump::singleton() args << QLatin1String("tests.dumper.CompositeSingleton") << QLatin1String("1.0") << QLatin1String("."); dumper.start(qmlplugindumpPath, args); - dumper.waitForFinished(); + QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString())); + QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString())); const QString &result = dumper.readAllStandardOutput(); - qDebug() << "result: " << result; - QVERIFY(result.contains(QLatin1String("exports: [\"Singleton 1.0\"]"))); - QVERIFY(result.contains(QLatin1String("exportMetaObjectRevisions: [0]"))); + QVERIFY2(result.contains(QLatin1String("exports: [\"Singleton 1.0\"]")), qPrintable(result)); + QVERIFY2(result.contains(QLatin1String("exportMetaObjectRevisions: [0]")), qPrintable(result)); } QTEST_MAIN(tst_qmlplugindump) diff --git a/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro b/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro index 4a2dde7c47..3adad3759b 100644 --- a/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro +++ b/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro @@ -1,3 +1,5 @@ TEMPLATE = subdirs -SUBDIRS = tst_qqmlapplicationengine.pro \ - testapp +SUBDIRS = testapp \ + tst_qqmlapplicationengine.pro + +CONFIG += ordered diff --git a/tests/auto/qml/qqmlbinding/data/bindingOverwriting.qml b/tests/auto/qml/qqmlbinding/data/bindingOverwriting.qml new file mode 100644 index 0000000000..767ca0c719 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/bindingOverwriting.qml @@ -0,0 +1,13 @@ +import QtQuick 2.9 + +Text { + visible: text && enabled + enabled: font.pixelSize === 25 + font: enabled ? Qt.font({ "pixelSize": 25 }) : Qt.font({ "pixelSize": 50 }) + + Component.onCompleted: { + enabled = Qt.binding(function() { return visible; }); // replacement binding, not breaking + visible = true; // breaks visible binding + font.bold = true; // breaks font binding + } +} diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index 6f1d82eca5..4b485d2ce8 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -50,6 +50,7 @@ private slots: void disabledOnUnknownProperty(); void disabledOnReadonlyProperty(); void delayed(); + void bindingOverwriting(); private: QQmlEngine engine; @@ -303,6 +304,21 @@ void tst_qqmlbinding::delayed() delete item; } +void tst_qqmlbinding::bindingOverwriting() +{ + QQmlTestMessageHandler messageHandler; + QLoggingCategory::setFilterRules(QStringLiteral("qt.qml.binding.removal.info=true")); + + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("bindingOverwriting.qml")); + QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QVERIFY(item); + delete item; + + QLoggingCategory::setFilterRules(QString()); + QCOMPARE(messageHandler.messages().count(), 2); +} + QTEST_MAIN(tst_qqmlbinding) #include "tst_qqmlbinding.moc" diff --git a/tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml b/tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml new file mode 100644 index 0000000000..462a9577ff --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml @@ -0,0 +1,15 @@ +import QtQuick 2.4 + +Item { + id: blaBlaBla + function hint() { + } + + Connections { + //target: blaBlaBla + //onHint: hint(); + on: true + } +} + + diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp index fe45495f74..22e9724c61 100644 --- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp +++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp @@ -54,6 +54,7 @@ private slots: void enableDisable_QTBUG_36350(); void disabledAtStart(); void clearImplicitTarget(); + void onWithoutASignal(); private: QQmlEngine engine; @@ -397,6 +398,15 @@ void tst_qqmlconnections::clearImplicitTarget() delete item; } +void tst_qqmlconnections::onWithoutASignal() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("connection-no-signal-name.qml")); + QVERIFY(c.isError()); // Cannot assign to non-existent property "on" expected + QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(c.create())); + QVERIFY(item == nullptr); // should parse error, and not give us an item (or crash). +} + QTEST_MAIN(tst_qqmlconnections) #include "tst_qqmlconnections.moc" diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicString.qml b/tests/auto/qml/qqmlecmascript/data/dynamicString.qml index 5693794c71..c704161eb5 100644 --- a/tests/auto/qml/qqmlecmascript/data/dynamicString.qml +++ b/tests/auto/qml/qqmlecmascript/data/dynamicString.qml @@ -11,6 +11,6 @@ MyTypeObject { date.setHours(5); date.setMinutes(30); date.setSeconds(50); - stringProperty = stringProperty.arg("Hello World").arg(false).arg(true).arg(100).arg(-100).arg(3.1415926).arg(Qt.formatDateTime(date, "yyyy-MM-dd hh::mm:ss")); + stringProperty = stringProperty.arg("Hello World").arg(false).arg(true).arg(100).arg(-100).arg(Math.PI).arg(Qt.formatDateTime(date, "yyyy-MM-dd hh::mm:ss")); } } diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml b/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml index 74c7cda9a3..c6732efc05 100644 --- a/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml +++ b/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml @@ -52,7 +52,11 @@ Item { function doStringTest(stringList, fn) { var expected = createExpected(stringList, fn); var actual = msc.strings(stringList); - return checkResults(expected, actual, fn); + var actual2 = msc.stringsVector(stringList); + var actual3 = msc.stringsStdVector(stringList); + return checkResults(expected, actual, fn) + && checkResults(expected, actual2, fn) + && checkResults(expected, actual3, fn) } function doIntTest(intList, fn) { var expected = createExpected(intList, fn); @@ -67,12 +71,16 @@ Item { function doIntVectorTest(intList, fn) { var expected = createExpected(intList, fn); var actual = msc.integerVector(intList); - return checkResults(expected, actual, fn); + var actual2 = msc.integerStdVector(intList); + return checkResults(expected, actual, fn) + && checkResults(expected, actual2, fn) } function doRealVectorTest(realList, fn) { var expected = createExpected(realList, fn); var actual = msc.realVector(realList); - return checkResults(expected, actual, fn); + var actual2 = msc.realStdVector(realList); + return checkResults(expected, actual, fn) + && checkResults(expected, actual2, fn) } function test_qtbug_25269(useCustomCompare) { diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index 63c2918325..c4692fdf31 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -283,6 +283,15 @@ public: { return stringList; } + Q_INVOKABLE QVector<QString> stringsVector(const QStringList& stringList) const + { + return stringList.toVector(); + } + Q_INVOKABLE + std::vector<QString> stringsStdVector(const QStringList& stringList) const + { + return std::vector<QString>(stringList.begin(), stringList.end()); + } Q_INVOKABLE QList<int> integers(QList<int> v) const { return v; @@ -299,14 +308,29 @@ public: { return v; } + Q_INVOKABLE + std::vector<int> integerStdVector(std::vector<int> v) const + { + return v; + } Q_INVOKABLE QVector<qreal> realVector(QVector<qreal> v) const { return v; } + Q_INVOKABLE + std::vector<qreal> realStdVector(std::vector<qreal> v) const + { + return v; + } Q_INVOKABLE QVector<bool> boolVector(QVector<bool> v) const { return v; } + Q_INVOKABLE + std::vector<bool> boolStdVector(std::vector<bool> v) const + { + return v; + } }; static MyInheritedQmlObject *theSingletonObject = 0; diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 7b9a43dc38..45f312e934 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -335,6 +336,9 @@ private slots: void stringify_qtbug_50592(); void instanceof_data(); void instanceof(); + void constkw_data(); + void constkw(); + void redefineGlobalProp(); void freeze_empty_object(); void singleBlockLoops(); void qtbug_60547(); @@ -4031,7 +4035,7 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Scope scope(v4); QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts.value()); - QV4::Scoped<QV4::QmlContextWrapper> qml(scope); + QV4::Scoped<QV4::QQmlContextWrapper> qml(scope); for (quint32 i = 0; i < scripts->getLength(); ++i) { QQmlContextData *scriptContext, *newContext; qml = scripts->getIndexed(i); @@ -8168,6 +8172,8 @@ void tst_qqmlecmascript::stringify_qtbug_50592() QCOMPARE(obj->property("source").toString(), QString::fromLatin1("http://example.org/some_nonexistant_image.png")); } +// Tests for the JS-only instanceof. Tests for the QML extensions for +// instanceof belong in tst_qqmllanguage! void tst_qqmlecmascript::instanceof_data() { QTest::addColumn<QString>("setupCode"); @@ -8230,6 +8236,108 @@ void tst_qqmlecmascript::instanceof() } } +void tst_qqmlecmascript::constkw_data() +{ + QTest::addColumn<QString>("sourceCode"); + QTest::addColumn<bool>("exceptionExpected"); + QTest::addColumn<QVariant>("expectedValue"); + + QTest::newRow("simpleconst") + << "const v = 5\n" + "v\n" + << false + << QVariant(5); + QTest::newRow("twoconst") + << "const v = 5, i = 10\n" + "v + i\n" + << false + << QVariant(15); + QTest::newRow("constandvar") + << "const v = 5\n" + "var i = 20\n" + "v + i\n" + << false + << QVariant(25); + QTest::newRow("const-multiple-scopes-same-var") + << "const v = 3\n" + "function f() { const v = 1; return v; }\n" + "v + f()\n" + << false + << QVariant(4); + + // error cases + QTest::newRow("const-no-initializer") + << "const v\n" + << true + << QVariant("SyntaxError: Missing initializer in const declaration"); + QTest::newRow("const-no-initializer-comma") + << "const v = 1, i\n" + << true + << QVariant("SyntaxError: Missing initializer in const declaration"); + QTest::newRow("const-no-duplicate") + << "const v = 1, v = 2\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("const-no-duplicate-2") + << "const v = 1\n" + "const v = 2\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("const-no-duplicate-var") + << "const v = 1\n" + "var v = 1\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("var-no-duplicate-const") + << "var v = 1\n" + "const v = 1\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("const-no-duplicate-let") + << "const v = 1\n" + "let v = 1\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("let-no-duplicate-const") + << "let v = 1\n" + "const v = 1\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); +} + +void tst_qqmlecmascript::constkw() +{ + QFETCH(QString, sourceCode); + QFETCH(bool, exceptionExpected); + QFETCH(QVariant, expectedValue); + + QJSEngine engine; + QJSValue ret = engine.evaluate(sourceCode); + + if (!exceptionExpected) { + QVERIFY2(!ret.isError(), qPrintable(ret.toString())); + QCOMPARE(ret.toVariant(), expectedValue); + } else { + QVERIFY2(ret.isError(), qPrintable(ret.toString())); + QCOMPARE(ret.toString(), expectedValue.toString()); + } +} + +// Redefine a property found on the global object. It shouldn't throw. +void tst_qqmlecmascript::redefineGlobalProp() +{ + { + QJSEngine engine; + QJSValue ret = engine.evaluate("\"use strict\"\n var toString = 1;"); + QVERIFY2(!ret.isError(), qPrintable(ret.toString())); + } + { + QJSEngine engine; + QJSValue ret = engine.evaluate("var toString = 1;"); + QVERIFY2(!ret.isError(), qPrintable(ret.toString())); + } +} + void tst_qqmlecmascript::freeze_empty_object() { // this shouldn't crash diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index dac6ddaebd..7aca830297 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -741,6 +741,9 @@ public: if (url.path().endsWith("Test.2/qmldir"))//Special case return QUrl::fromLocalFile(m_base.path() + "interception/module/intercepted/qmldir"); + // Special case: with 5.10 we always add the implicit import, so we need to explicitly handle this case now + if (url.path().endsWith("intercepted/qmldir")) + return url; QString alteredPath = url.path(); int a = alteredPath.lastIndexOf('/'); diff --git a/tests/auto/qml/qqmllanguage/data/TypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/TypeWithEnum.qml new file mode 100644 index 0000000000..c89a228bef --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/TypeWithEnum.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + EnumValue2, + EnumValue3 + } + + property int enumValue: TypeWithEnum.EnumValue2 + property int enumValue2 + property int scopedEnumValue: TypeWithEnum.MyEnum.EnumValue2 + Component.onCompleted: enumValue2 = TypeWithEnum.EnumValue3 +} diff --git a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml index efedf2b14a..48f7eb6715 100644 --- a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml +++ b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml @@ -2,4 +2,5 @@ import Test 1.0 MyNamespacedType { myEnum: MyNamespace.Key5 + property int intProperty: MyNamespace.MyOtherNSEnum.OtherKey2 } diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/CustomMouseArea.qml b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomMouseArea.qml new file mode 100644 index 0000000000..f6ec5848c1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomMouseArea.qml @@ -0,0 +1,6 @@ +import QtQuick 2.6 + +MouseArea { + +} + diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangle.qml b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangle.qml new file mode 100644 index 0000000000..b3fa43a671 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangle.qml @@ -0,0 +1,4 @@ +import QtQuick 2.6 + +Rectangle { +} diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangleWithProp.qml b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangleWithProp.qml new file mode 100644 index 0000000000..cf566b9315 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangleWithProp.qml @@ -0,0 +1,6 @@ +import QtQuick 2.6 + +Rectangle { + property int somethingCustom: 0 +} + diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/qmldir b/tests/auto/qml/qqmllanguage/data/instanceOf/qmldir new file mode 100644 index 0000000000..144c93d8e3 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/qmldir @@ -0,0 +1,2 @@ +CustomRectangle 1.0 CustomRectangle.qml +CustomMouseArea 1.0 CustomMouseArea.qml diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtqml.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml.qml new file mode 100644 index 0000000000..d74b172cf8 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml.qml @@ -0,0 +1,13 @@ +import QtQml 2.0 + +QtObject { + id: qtobjectInstance + + property Timer aTimer: Timer { + id: timerInstance + } + + property Connections aConnections: Connections { + id: connectionsInstance + } +} diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtqml_qualified.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml_qualified.qml new file mode 100644 index 0000000000..a8e303363e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml_qualified.qml @@ -0,0 +1,13 @@ +import QtQml 2.0 as QmlImport + +QmlImport.QtObject { + id: qtobjectInstance + + property QmlImport.Timer aTimer: QmlImport.Timer { + id: timerInstance + } + + property QmlImport.Connections aConnections: QmlImport.Connections { + id: connectionsInstance + } +} diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtquick.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick.qml new file mode 100644 index 0000000000..9c1808d515 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Item { + id: itemInstance + + Rectangle { + id: rectangleInstance + } + + MouseArea { + id: mouseAreaInstance + } +} + diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite.qml new file mode 100644 index 0000000000..78fc112805 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import "instanceOf" + +Item { + id: itemInstance + + Rectangle { + id: rectangleInstance + } + + MouseArea { + id: mouseAreaInstance + } + + CustomRectangle { + id: customRectangleInstance + } + CustomRectangleWithProp { + id: customRectangleWithPropInstance + } + CustomMouseArea { + id: customMouseAreaInstance + } +} + + diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite_qualified.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite_qualified.qml new file mode 100644 index 0000000000..97361b7334 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite_qualified.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 as QuickImport +import "instanceOf" as CustomImport + +QuickImport.Item { + id: itemInstance + + QuickImport.Rectangle { + id: rectangleInstance + } + + QuickImport.MouseArea { + id: mouseAreaInstance + } + + CustomImport.CustomRectangle { + id: customRectangleInstance + } + CustomImport.CustomRectangleWithProp { + id: customRectangleWithPropInstance + } + CustomImport.CustomMouseArea { + id: customMouseAreaInstance + } +} + + + diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/NonSingletonType.qml b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/NonSingletonType.qml new file mode 100644 index 0000000000..ec7c76c055 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/NonSingletonType.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 +import org.qtproject.MixedModule 1.0 + +Item { +} diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml new file mode 100644 index 0000000000..7763c783f1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 +pragma Singleton + +Item { +} diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/qmldir b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/qmldir new file mode 100644 index 0000000000..cd03a5f941 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/qmldir @@ -0,0 +1,4 @@ +module org.qtproject.MixedModule +singleton SingletonType 1.0 SingletonType.qml +NonSingletonType 1.0 NonSingletonType.qml +Test 1.0 test.js diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/test.js b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/test.js new file mode 100644 index 0000000000..6a53b53b02 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/test.js @@ -0,0 +1 @@ +var foo = 1 diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.errors.txt b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.errors.txt new file mode 100644 index 0000000000..d1bd2bcff4 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.errors.txt @@ -0,0 +1 @@ +6:9:Enum names must begin with an upper case letter diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.qml b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.qml new file mode 100644 index 0000000000..0b50820128 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + enumValue2, + EnumValue3 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.errors.txt b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.errors.txt new file mode 100644 index 0000000000..3e051c416e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.errors.txt @@ -0,0 +1 @@ +4:5:Scoped enum names must begin with an upper case letter diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.qml b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.qml new file mode 100644 index 0000000000..bb7aea6aa4 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum myEnum { + EnumValue1, + EnumValue2, + EnumValue3 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/mixedModuleWithSelfImport.qml b/tests/auto/qml/qqmllanguage/data/mixedModuleWithSelfImport.qml new file mode 100644 index 0000000000..7768a6aedf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/mixedModuleWithSelfImport.qml @@ -0,0 +1,3 @@ +import org.qtproject.MixedModule 1.0 + +NonSingletonType {} diff --git a/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml index 5f8c11e5f6..b6a07693f2 100644 --- a/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml +++ b/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml @@ -3,4 +3,5 @@ import Test 1.0 RegisteredCompositeTypeWithEnum { property int enumValue0: RegisteredCompositeTypeWithEnum.EnumValue0 property int enumValue42: RegisteredCompositeTypeWithEnum.EnumValue42 + property int enumValue15: RegisteredCompositeTypeWithEnum.ScopedCompositeEnum.EnumValue15 } diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnum.qml b/tests/auto/qml/qqmllanguage/data/scopedEnum.qml new file mode 100644 index 0000000000..7f4177af76 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scopedEnum.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 +import Test 1.0 + +MyTypeObject { + id: obj + scopedEnum: MyTypeObject.MyScopedEnum.ScopedVal1 + intProperty: MyTypeObject.MyScopedEnum.ScopedVal2 + property int listValue: myModel.get(0).myData + property int noScope: MyTypeObject.ScopedVal1 + + function assignNewValue() { + scopedEnum = MyTypeObject.MyScopedEnum.ScopedVal2 + noScope = MyTypeObject.ScopedVal2 + } + + property ListModel myModel: ListModel { + ListElement { + myData: MyTypeObject.MyScopedEnum.ScopedVal3 + } + } +} diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumList.errors.txt b/tests/auto/qml/qqmllanguage/data/scopedEnumList.errors.txt new file mode 100644 index 0000000000..67576dfd8d --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scopedEnumList.errors.txt @@ -0,0 +1 @@ +7:13:ListElement: cannot use script for property value diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumList.qml b/tests/auto/qml/qqmllanguage/data/scopedEnumList.qml new file mode 100644 index 0000000000..8655139683 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scopedEnumList.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import Test 1.0 + +MyTypeObject { + property ListModel myModel: ListModel { + ListElement { + myData: MyTypeObject.MyScopedEnum + } + } +} diff --git a/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml new file mode 100644 index 0000000000..2509fc0df1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + property int enumValue: TypeWithEnum.EnumValue2 + property int enumValue2: -1 + property int scopedEnumValue: TypeWithEnum.MyEnum.EnumValue3 + Component.onCompleted: enumValue2 = TypeWithEnum.EnumValue1 +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 7d7a8ac6d3..e4a76b4324 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -223,6 +223,7 @@ class MyTypeObject : public QObject Q_PROPERTY(Qt::TextFormat qtEnumProperty READ qtEnumProperty WRITE setQtEnumProperty NOTIFY qtEnumPropertyChanged) Q_PROPERTY(MyMirroredEnum mirroredEnumProperty READ mirroredEnumProperty WRITE setMirroredEnumProperty NOTIFY mirroredEnumPropertyChanged) Q_PROPERTY(MyEnumContainer::RelatedEnum relatedEnumProperty READ relatedEnumProperty WRITE setRelatedEnumProperty) + Q_PROPERTY(MyScopedEnum scopedEnum READ scopedEnum WRITE setScopedEnum) Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringPropertyChanged) Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty NOTIFY byteArrayPropertyChanged) Q_PROPERTY(uint uintProperty READ uintProperty WRITE setUintProperty NOTIFY uintPropertyChanged) @@ -339,6 +340,14 @@ public: relatedEnumPropertyValue = v; } + enum class MyScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3 }; + Q_ENUM(MyScopedEnum) + MyScopedEnum scopedEnumPropertyValue; + MyScopedEnum scopedEnum() const { return scopedEnumPropertyValue; } + void setScopedEnum(MyScopedEnum v) { + scopedEnumPropertyValue = v; + } + QString stringPropertyValue; QString stringProperty() const { return stringPropertyValue; @@ -738,6 +747,13 @@ namespace MyNamespace { }; Q_ENUM_NS(MyNSEnum); + enum class MyOtherNSEnum { + OtherKey1 = 1, + OtherKey2 + }; + Q_ENUM_NS(MyOtherNSEnum); + + class MyNamespacedType : public QObject { Q_OBJECT @@ -1171,9 +1187,11 @@ class MyCompositeBaseType : public QObject { Q_OBJECT Q_ENUMS(CompositeEnum) + Q_ENUMS(ScopedCompositeEnum) public: enum CompositeEnum { EnumValue0, EnumValue42 = 42 }; + enum class ScopedCompositeEnum : int { EnumValue15 = 15 }; static QObject *qmlAttachedProperties(QObject *parent) { return new QObject(parent); } }; diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 39f5082c70..c145c6d737 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -178,6 +178,7 @@ private slots: void importIncorrectCase(); void importJs_data(); void importJs(); + void explicitSelfImport(); void qmlAttachedPropertiesObjectMethod(); void customOnProperty(); @@ -208,6 +209,8 @@ private slots: void lowercaseEnumRuntime(); void lowercaseEnumCompileTime_data(); void lowercaseEnumCompileTime(); + void scopedEnum(); + void qmlEnums(); void literals_data(); void literals(); @@ -263,6 +266,9 @@ private slots: void qmlTypeCanBeResolvedByName_data(); void qmlTypeCanBeResolvedByName(); + void instanceof_data(); + void instanceof(); + void concurrentLoadQmlDir(); private: @@ -311,7 +317,7 @@ private: if (!errorfile) { \ if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \ qWarning() << "Unexpected Errors:" << component.errors(); \ - QVERIFY(!component.isError()); \ + QVERIFY2(!component.isError(), qPrintable(component.errorString())); \ QVERIFY(component.errors().isEmpty()); \ } else { \ DETERMINE_ERRORS(errorfile,expected,actual);\ @@ -540,6 +546,10 @@ void tst_qqmllanguage::errors_data() QTest::newRow("singularProperty") << "singularProperty.qml" << "singularProperty.errors.txt" << false; QTest::newRow("singularProperty.2") << "singularProperty.2.qml" << "singularProperty.2.errors.txt" << false; + QTest::newRow("scopedEnumList") << "scopedEnumList.qml" << "scopedEnumList.errors.txt" << false; + QTest::newRow("lowercase enum value") << "lowercaseQmlEnum.1.qml" << "lowercaseQmlEnum.1.errors.txt" << false; + QTest::newRow("lowercase enum type") << "lowercaseQmlEnum.2.qml" << "lowercaseQmlEnum.2.errors.txt" << false; + const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ? QStringLiteral("incorrectCase.errors.sensitive.txt") : QStringLiteral("incorrectCase.errors.insensitive.txt"); @@ -1600,6 +1610,9 @@ void tst_qqmllanguage::cppnamespace() VERIFY_ERRORS(0); QObject *object = component.create(); QVERIFY(object != 0); + + QCOMPARE(object->property("intProperty").toInt(), (int)MyNamespace::MyOtherNSEnum::OtherKey2); + delete object; } @@ -3058,12 +3071,22 @@ void tst_qqmllanguage::importJs() engine.setImportPathList(defaultImportPathList); } +void tst_qqmllanguage::explicitSelfImport() +{ + engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); + + QQmlComponent component(&engine, testFileUrl("mixedModuleWithSelfImport.qml")); + QVERIFY(component.errors().count() == 0); + + engine.setImportPathList(defaultImportPathList); +} + void tst_qqmllanguage::qmlAttachedPropertiesObjectMethod() { QObject object; QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(&object, false), (QObject *)0); - QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(&object, true), (QObject *)0); + QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(&object, true)); { QQmlComponent component(&engine, testFileUrl("qmlAttachedPropertiesObjectMethod.1.qml")); @@ -3500,6 +3523,7 @@ void tst_qqmllanguage::registeredCompositeTypeWithEnum() QCOMPARE(o->property("enumValue0").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue0)); QCOMPARE(o->property("enumValue42").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue42)); + QCOMPARE(o->property("enumValue15").toInt(), static_cast<int>(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15)); delete o; } @@ -3680,6 +3704,44 @@ void tst_qqmllanguage::lowercaseEnumCompileTime() VERIFY_ERRORS(qPrintable(errorFile)); } +void tst_qqmllanguage::scopedEnum() +{ + QQmlComponent component(&engine, testFileUrl("scopedEnum.qml")); + + MyTypeObject *o = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(o != 0); + + QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal1); + QCOMPARE(o->intProperty(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); + QCOMPARE(o->property("listValue").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal3); + QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal1); + + QMetaObject::invokeMethod(o, "assignNewValue"); + QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal2); + QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); +} + +void tst_qqmllanguage::qmlEnums() +{ + { + QQmlComponent component(&engine, testFileUrl("TypeWithEnum.qml")); + QObject *o = component.create(); + QVERIFY(o); + QCOMPARE(o->property("enumValue").toInt(), 1); + QCOMPARE(o->property("enumValue2").toInt(), 2); + QCOMPARE(o->property("scopedEnumValue").toInt(), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("usingTypeWithEnum.qml")); + QObject *o = component.create(); + QVERIFY(o); + QCOMPARE(o->property("enumValue").toInt(), 1); + QCOMPARE(o->property("enumValue2").toInt(), 0); + QCOMPARE(o->property("scopedEnumValue").toInt(), 2); + } +} + void tst_qqmllanguage::literals_data() { QTest::addColumn<QString>("property"); @@ -4340,6 +4402,200 @@ void tst_qqmllanguage::qmlTypeCanBeResolvedByName() QVERIFY(!o.isNull()); } +// Tests for the QML-only extensions of instanceof. Tests for the regular JS +// instanceof belong in tst_qqmlecmascript! +void tst_qqmllanguage::instanceof_data() +{ + QTest::addColumn<QUrl>("documentToTestIn"); + QTest::addColumn<QVariant>("expectedValue"); + + // so the way this works is that the name of the test tag defines the test + // to run. + // + // the expectedValue is either a boolean true or false for whether the two + // operands are indeed an instanceof each other, or a string for the + // expected error message. + + // assert that basic types don't convert to QObject + QTest::newRow("1 instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("true instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("\"foobar\" instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + + // assert that Managed don't either + QTest::newRow("new String(\"foobar\") instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("new Object() instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("new Date() instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + + // test that simple QtQml comparisons work + QTest::newRow("qtobjectInstance instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("qtobjectInstance instanceof Timer") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(false); + QTest::newRow("timerInstance instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("timerInstance instanceof Timer") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof Timer") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(false); + QTest::newRow("connectionsInstance instanceof Connections") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + + // make sure they still work when imported with a qualifier + QTest::newRow("qtobjectInstance instanceof QmlImport.QtObject") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("qtobjectInstance instanceof QmlImport.Timer") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(false); + QTest::newRow("timerInstance instanceof QmlImport.QtObject") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("timerInstance instanceof QmlImport.Timer") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof QmlImport.QtObject") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof QmlImport.Timer") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(false); + QTest::newRow("connectionsInstance instanceof QmlImport.Connections") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + + // test that Quick C++ types work ok + QTest::newRow("itemInstance instanceof QtObject") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("itemInstance instanceof Timer") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("itemInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("rectangleInstance instanceof Item") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("rectangleInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("rectangleInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("mouseAreaInstance instanceof Item") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("mouseAreaInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("mouseAreaInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + + // test that unqualified quick composite types work ok + QTest::newRow("rectangleInstance instanceof CustomRectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(false); + QTest::newRow("customRectangleInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof Item") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomRectangleWithProp") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomRectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(false); // ### XXX: QTBUG-58477 + QTest::newRow("customRectangleWithPropInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(false); + QTest::newRow("customMouseAreaInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + + // test that they still work when qualified + QTest::newRow("rectangleInstance instanceof CustomImport.CustomRectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(false); + QTest::newRow("customRectangleInstance instanceof QuickImport.Rectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof QuickImport.Item") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomImport.CustomRectangleWithProp") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(false); // ### XXX: QTBUG-58477 + QTest::newRow("customRectangleWithPropInstance instanceof QuickImport.Rectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof QuickImport.MouseArea") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(false); + QTest::newRow("customMouseAreaInstance instanceof QuickImport.MouseArea") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); +} + +void tst_qqmllanguage::instanceof() +{ + QFETCH(QUrl, documentToTestIn); + QFETCH(QVariant, expectedValue); + + QQmlEngine engine; + QQmlComponent component(&engine, documentToTestIn); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != 0); + + QQmlExpression expr(engine.contextForObject(o.data()), 0, QString::fromLatin1(QTest::currentDataTag())); + QVariant ret = expr.evaluate(); + + if (expectedValue.type() == QVariant::Bool) { + // no error expected + QVERIFY2(!expr.hasError(), qPrintable(expr.error().description())); + bool returnValue = ret.toBool(); + + if (QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomRectangle") || + QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle")) + QEXPECT_FAIL("", "QTBUG-58477: QML type rules are a little lax", Continue); + QCOMPARE(returnValue, expectedValue.toBool()); + } else { + QVERIFY(expr.hasError()); + QCOMPARE(expr.error().description(), expectedValue.toString()); + } +} + void tst_qqmllanguage::concurrentLoadQmlDir() { ThreadedTestHTTPServer server(dataDirectory()); diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 555ca5713e..e442dd1421 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -1204,8 +1204,8 @@ void tst_qqmllistmodel::role_mode_data() QTest::newRow("default1") << "{append({'a':1});dynamicRoles}" << 0 << ""; QTest::newRow("enableDynamic0") << "{dynamicRoles=true;dynamicRoles}" << 1 << ""; - QTest::newRow("enableDynamic1") << "{append({'a':1});dynamicRoles=true;dynamicRoles}" << 0 << "<Unknown File>: QML ListModel: unable to enable dynamic roles as this model is not empty!"; - QTest::newRow("enableDynamic2") << "{dynamicRoles=true;append({'a':1});dynamicRoles=false;dynamicRoles}" << 1 << "<Unknown File>: QML ListModel: unable to enable static roles as this model is not empty!"; + QTest::newRow("enableDynamic1") << "{append({'a':1});dynamicRoles=true;dynamicRoles}" << 0 << "<Unknown File>: QML ListModel: unable to enable dynamic roles as this model is not empty"; + QTest::newRow("enableDynamic2") << "{dynamicRoles=true;append({'a':1});dynamicRoles=false;dynamicRoles}" << 1 << "<Unknown File>: QML ListModel: unable to enable static roles as this model is not empty"; } void tst_qqmllistmodel::role_mode() diff --git a/tests/auto/qml/qqmltranslation/data/TranslationChangeBase.qml b/tests/auto/qml/qqmltranslation/data/TranslationChangeBase.qml new file mode 100644 index 0000000000..c447c84987 --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/TranslationChangeBase.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string baseProperty: qsTr("translate me"); +} diff --git a/tests/auto/qml/qqmltranslation/data/translationChange.qml b/tests/auto/qml/qqmltranslation/data/translationChange.qml new file mode 100644 index 0000000000..23b87c2493 --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/translationChange.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 + +TranslationChangeBase { + baseProperty: "do not translate" + property string text1: qsTr("translate me") + function weDoTranslations() { + return qsTr("translate me") + } + property string text2: weDoTranslations() +} diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 1fc803a395..1c9523fc38 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -44,6 +44,7 @@ private slots: void translation_data(); void translation(); void idTranslation(); + void translationChange(); }; void tst_qqmltranslation::translation_data() @@ -162,6 +163,51 @@ void tst_qqmltranslation::idTranslation() delete object; } +class DummyTranslator : public QTranslator +{ + Q_OBJECT + + QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override + { + Q_UNUSED(context); + Q_UNUSED(disambiguation); + Q_UNUSED(n); + if (!qstrcmp(sourceText, "translate me")) + return QString::fromUtf8("xxx"); + return QString(); + } + + bool isEmpty() const override + { + return false; + } +}; + +void tst_qqmltranslation::translationChange() +{ + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("translationChange.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("baseProperty").toString(), QString::fromUtf8("do not translate")); + QCOMPARE(object->property("text1").toString(), QString::fromUtf8("translate me")); + QCOMPARE(object->property("text2").toString(), QString::fromUtf8("translate me")); + + DummyTranslator translator; + QCoreApplication::installTranslator(&translator); + + QEvent ev(QEvent::LanguageChange); + QCoreApplication::sendEvent(&engine, &ev); + + QCOMPARE(object->property("baseProperty").toString(), QString::fromUtf8("do not translate")); + QCOMPARE(object->property("text1").toString(), QString::fromUtf8("xxx")); + QCOMPARE(object->property("text2").toString(), QString::fromUtf8("xxx")); + + QCoreApplication::removeTranslator(&translator); +} + QTEST_MAIN(tst_qqmltranslation) #include "tst_qqmltranslation.moc" diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect Binary files differnew file mode 100644 index 0000000000..6d34e1d2bb --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml new file mode 100644 index 0000000000..ba9761201e --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +QtObject { + property string url + + property bool dataOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("POST", url); + x.setRequestHeader("Accept-Language","en-US"); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + dataOK = (x.responseText == "QML Rocks!\n"); + } + } + + var data = new Uint8Array([1, 2, 3, 0, 3, 2, 1]) + x.send(data.buffer); + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 1ce07ecdab..a8a6456dff 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -597,6 +597,7 @@ void tst_qqmlxmlhttprequest::send_withdata_data() QTest::newRow("Incorrect content-type - out of order") << "send_data.4.expect" << "send_data.5.qml"; QTest::newRow("PUT") << "send_data.6.expect" << "send_data.6.qml"; QTest::newRow("Correct content-type - no charset") << "send_data.1.expect" << "send_data.7.qml"; + QTest::newRow("ArrayBuffer") << "send_data.11.expect" << "send_data.11.qml"; } void tst_qqmlxmlhttprequest::send_options() diff --git a/tests/auto/qmltest/BLACKLIST b/tests/auto/qmltest/BLACKLIST deleted file mode 100644 index 9b39f87219..0000000000 --- a/tests/auto/qmltest/BLACKLIST +++ /dev/null @@ -1,16 +0,0 @@ -# Blacklist for testing -[SelfTests::test_blacklisted_fail] -* -[SelfTests::test_blacklistWithData:test2] -* -[shadersource-dynamic-sourceobject::test_endresult] -linux -[tst_grabImage::test_equals] -linux -[ListView::test_listInteractiveCurrentIndexEnforce] -linux -osx-10.12 -[TextEdit::test_textentry] -osx-10.12 -[TextEdit::test_textentry_char] -osx-10.12 diff --git a/tests/auto/qmltest/animatedimage/animatedimage.pro b/tests/auto/qmltest/animatedimage/animatedimage.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/animatedimage/animatedimage.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/animations/animations.pro b/tests/auto/qmltest/animations/animations.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/animations/animations.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/borderimage/borderimage.pro b/tests/auto/qmltest/borderimage/borderimage.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/borderimage/borderimage.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/buttonclick/buttonclick.pro b/tests/auto/qmltest/buttonclick/buttonclick.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/buttonclick/buttonclick.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/createbenchmark/createbenchmark.pro b/tests/auto/qmltest/createbenchmark/createbenchmark.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/createbenchmark/createbenchmark.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/events/events.pro b/tests/auto/qmltest/events/events.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/events/events.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/events/tst_touch.qml b/tests/auto/qmltest/events/tst_touch.qml index 5b209a6d0b..fd603e5a71 100644 --- a/tests/auto/qmltest/events/tst_touch.qml +++ b/tests/auto/qmltest/events/tst_touch.qml @@ -35,6 +35,16 @@ MultiPointTouchArea { width: 100 height: 100 + // touchUpdatedSpy stores the QQuickTouchPoint, and in some cases + // MultiPointTouchArea can delete it out from under us. + // (test_simpleChain was failing because touchUpdatedSpy.signalArguments[0][0][0] + // ended up as an empty object somehow.) If we declare + // all the touchpoints that this test will use, that won't happen. + touchPoints: [ + TouchPoint { }, + TouchPoint { } + ] + SignalSpy { id: touchUpdatedSpy target: touchArea diff --git a/tests/auto/qmltest/fontloader/fontloader.pro b/tests/auto/qmltest/fontloader/fontloader.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/fontloader/fontloader.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/gradient/gradient.pro b/tests/auto/qmltest/gradient/gradient.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/gradient/gradient.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/image/image.pro b/tests/auto/qmltest/image/image.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/image/image.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/image/logo.pkm b/tests/auto/qmltest/image/logo.pkm Binary files differnew file mode 100644 index 0000000000..c0987c5c36 --- /dev/null +++ b/tests/auto/qmltest/image/logo.pkm diff --git a/tests/auto/qmltest/image/tst_image.qml b/tests/auto/qmltest/image/tst_image.qml index 6f2164363c..69ef4d1868 100644 --- a/tests/auto/qmltest/image/tst_image.qml +++ b/tests/auto/qmltest/image/tst_image.qml @@ -112,6 +112,11 @@ Item { fillMode: Image.TileHorizontally } + Image { + id: pkmImage + source: "logo.pkm" + } + TestCase { name: "Image" @@ -212,5 +217,9 @@ Item { compare(tileModes3.fillMode, Image.TileHorizontally) } + function test_pkmImage() { + compare(pkmImage.width, 256) + compare(pkmImage.height, 256) + } } } diff --git a/tests/auto/qmltest/itemgrabber/itemgrabber.pro b/tests/auto/qmltest/itemgrabber/itemgrabber.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/itemgrabber/itemgrabber.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml index a80814d6de..53ed3658c2 100644 --- a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml +++ b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml @@ -135,7 +135,7 @@ Item { property int callCount: 0; property bool ready: false; function handleGrab(result) { - if (!result.saveToFile("itemgrabber/image.png")) + if (!result.saveToFile("image.png")) print("Error: Failed to save image to disk..."); source = "image.png"; ready = true; @@ -149,7 +149,7 @@ Item { y: 0 property bool ready: false; function handleGrab(result) { - if (!result.saveToFile("itemgrabber/image_small.png")) + if (!result.saveToFile("image_small.png")) print("Error: Failed to save image to disk..."); source = "image_small.png"; ready = true; diff --git a/tests/auto/qmltest/layout/layout.pro b/tests/auto/qmltest/layout/layout.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/layout/layout.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/listmodel/listmodel.pro b/tests/auto/qmltest/listmodel/listmodel.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/listmodel/listmodel.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/listview/BLACKLIST b/tests/auto/qmltest/listview/BLACKLIST new file mode 100644 index 0000000000..62bf89128d --- /dev/null +++ b/tests/auto/qmltest/listview/BLACKLIST @@ -0,0 +1,3 @@ +# Blacklist for testing +[ListView::test_listInteractiveCurrentIndexEnforce] +* diff --git a/tests/auto/qmltest/listview/listview.pro b/tests/auto/qmltest/listview/listview.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/listview/listview.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/objectmodel/objectmodel.pro b/tests/auto/qmltest/objectmodel/objectmodel.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/objectmodel/objectmodel.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/pathview/pathview.pro b/tests/auto/qmltest/pathview/pathview.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/pathview/pathview.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/pixel/pixel.pro b/tests/auto/qmltest/pixel/pixel.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/pixel/pixel.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/positioners/positioners.pro b/tests/auto/qmltest/positioners/positioners.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/positioners/positioners.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index 52fd6bf9de..8ad1541cbc 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -1,14 +1,30 @@ -TEMPLATE=app -TARGET=tst_qmltest -CONFIG += qmltestcase -CONFIG += console -SOURCES += tst_qmltest.cpp - - -importFiles.files = borderimage buttonclick createbenchmark events qqmlbinding selftests - -importFiles.path = . -DEPLOYMENT += importFiles - -# Please do not make this test insignificant again, thanks. -# Just skip those unstable ones. See also QTBUG-33723. +TEMPLATE = subdirs +SUBDIRS += \ + animatedimage \ + animations \ + borderimage \ + buttonclick \ + createbenchmark \ + events \ + fontloader \ + gradient \ + image \ + itemgrabber \ + layout \ + listmodel \ + listview \ + objectmodel \ + pathview \ + pixel \ + positioners \ + qqmlbinding \ + qtbug46798 \ + rectangle \ + selftests \ + shadersource \ + stability \ + statemachine \ + text \ + textedit \ + textinput \ + window diff --git a/tests/auto/qmltest/qqmlbinding/qqmlbinding.pro b/tests/auto/qmltest/qqmlbinding/qqmlbinding.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/qqmlbinding/qqmlbinding.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/qtbug46798/qtbug46798.pro b/tests/auto/qmltest/qtbug46798/qtbug46798.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/qtbug46798/qtbug46798.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/rectangle/rectangle.pro b/tests/auto/qmltest/rectangle/rectangle.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/rectangle/rectangle.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/selftests/BLACKLIST b/tests/auto/qmltest/selftests/BLACKLIST new file mode 100644 index 0000000000..ffce42e4c0 --- /dev/null +++ b/tests/auto/qmltest/selftests/BLACKLIST @@ -0,0 +1,13 @@ +# Blacklist for testing +[SelfTests::test_blacklisted_fail] +* +[SelfTests::test_blacklistWithData:test2] +* +# QTBUG-53793: seems to be failing on Linux a little too often... +[tst_grabImage::test_equals] +linux +[tst_grabImage::test_sizeProps] +linux +[tst_grabImage::test_save] +linux + diff --git a/tests/auto/qmltest/selftests/selftests.pro b/tests/auto/qmltest/selftests/selftests.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/selftests/selftests.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/selftests/tst_grabImage.qml b/tests/auto/qmltest/selftests/tst_grabImage.qml index 1748030f14..7ce7e93a07 100644 --- a/tests/auto/qmltest/selftests/tst_grabImage.qml +++ b/tests/auto/qmltest/selftests/tst_grabImage.qml @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -35,7 +36,7 @@ TestCase { when: windowShown function test_equals() { - var rect = Qt.createQmlObject("import QtQuick 2.0; Rectangle { color: 'red'; width: 10; height: 10; }", testCase); + var rect = createTemporaryQmlObject("import QtQuick 2.0; Rectangle { color: 'red'; width: 10; height: 10; }", testCase); verify(rect); var oldImage = grabImage(rect); rect.width += 10; @@ -45,9 +46,66 @@ TestCase { oldImage = grabImage(rect); // Don't change anything... newImage = grabImage(rect); - verify(newImage.equals(oldImage)); + try { + compare(newImage.size, oldImage.size); + verify(newImage.equals(oldImage)); + } catch (ex) { + oldImage.save("tst_grabImage_test_equals_oldImage.png") + newImage.save("tst_grabImage_test_equals_newImage.png") + throw ex; + } verify(!newImage.equals(null)); verify(!newImage.equals(undefined)); } + + function test_sizeProps() { + var rect = createTemporaryQmlObject("import QtQuick 2.0; Rectangle { color: 'red'; width: 10; height: 20; }", testCase); + var image = grabImage(rect); + + try { + compare(image.width, 10) + compare(image.height, 20) + compare(image.size, Qt.size(10, 20)) + } catch (ex) { + image.save("tst_grabImage_test_sizeProps.png") + throw ex; + } + } + + function test_save() { + var rect = createTemporaryQmlObject("import QtQuick 2.0; Rectangle { color: 'red'; width: 10; height: 20; }", testCase); + var grabbedImage = grabImage(rect); + grabbedImage.save("tst_grabImage_test_save.png") + + // Now try to load it + var url = Qt.resolvedUrl("tst_grabImage_test_save.png") + var image = createTemporaryQmlObject("import QtQuick 2.0; Image { source: \"" + url + "\" }", testCase); + tryCompare(image, "status", Image.Ready) + var grabbedImage2 = grabImage(image); + + try { + verify(grabbedImage2.equals(grabbedImage)) + } catch (ex) { + grabbedImage2.save("tst_grabImage_test_save2.png") + throw ex; + } + } + + function test_saveThrowsWhenFailing() { + var rect = createTemporaryQmlObject("import QtQuick 2.0; Rectangle { color: 'red'; width: 10; height: 20; }", testCase); + var grabbedImage = grabImage(rect); + var didThrow = false; + + try { + // Format doesn't exist, so this will throw + grabbedImage.save("tst_grabImage_test_saveThrowsWhenFailing.never-gonna-give-you-up"); + } catch (ex) { + didThrow = true; + } + + if (!didThrow) { + fail("save() should have thrown, but didn't!") + } + } } diff --git a/tests/auto/qmltest/selftests/tst_selftests.qml b/tests/auto/qmltest/selftests/tst_selftests.qml index 439ea7a70d..5555876014 100644 --- a/tests/auto/qmltest/selftests/tst_selftests.qml +++ b/tests/auto/qmltest/selftests/tst_selftests.qml @@ -167,6 +167,16 @@ TestCase { caught = true } verify(caught) + + caught = false; + try { + testCase.verify(true, "foo", "bar") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "More than two arguments given to verify(). Did you mean tryVerify() or tryCompare()?") + caught = true + } + verify(caught) } function test_compare() { diff --git a/tests/auto/qmltest/shadersource/BLACKLIST b/tests/auto/qmltest/shadersource/BLACKLIST new file mode 100644 index 0000000000..cc1e110153 --- /dev/null +++ b/tests/auto/qmltest/shadersource/BLACKLIST @@ -0,0 +1,3 @@ +# Blacklist for testing +[shadersource-dynamic-sourceobject::test_endresult] +linux diff --git a/tests/auto/qmltest/shadersource/shadersource.pro b/tests/auto/qmltest/shadersource/shadersource.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/shadersource/shadersource.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/stability/stability.pro b/tests/auto/qmltest/stability/stability.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/stability/stability.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/statemachine/statemachine.pro b/tests/auto/qmltest/statemachine/statemachine.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/statemachine/statemachine.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/text/text.pro b/tests/auto/qmltest/text/text.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/text/text.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/textedit/BLACKLIST b/tests/auto/qmltest/textedit/BLACKLIST new file mode 100644 index 0000000000..e06cba3e8f --- /dev/null +++ b/tests/auto/qmltest/textedit/BLACKLIST @@ -0,0 +1,6 @@ +# Blacklist for testing +[TextEdit::test_textentry] +osx-10.12 +[TextEdit::test_textentry_char] +osx-10.12 + diff --git a/tests/auto/qmltest/textedit/textedit.pro b/tests/auto/qmltest/textedit/textedit.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/textedit/textedit.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/textinput/textinput.pro b/tests/auto/qmltest/textinput/textinput.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/textinput/textinput.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/qmltest/tst_qmltest.cpp b/tests/auto/qmltest/tst_qmltest.cpp deleted file mode 100644 index 3387ce8ee9..0000000000 --- a/tests/auto/qmltest/tst_qmltest.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtQuickTest/quicktest.h> -QUICK_TEST_MAIN(qmltest)
\ No newline at end of file diff --git a/tests/auto/qmltest/window/window.pro b/tests/auto/qmltest/window/window.pro new file mode 100644 index 0000000000..a7938e7003 --- /dev/null +++ b/tests/auto/qmltest/window/window.pro @@ -0,0 +1 @@ +CONFIG += qmltestcase diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index b6742b9efe..2dc5a8ed23 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -86,6 +86,8 @@ tst_examples::tst_examples() excludedDirs << "snippets/qml/visualdatamodel_rootindex"; excludedDirs << "snippets/qml/qtbinding"; excludedDirs << "snippets/qml/imports"; + excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources + excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources #ifdef QT_NO_XMLPATTERNS excludedDirs << "demos/twitter"; diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp index ad77743ddd..e6655589a3 100644 --- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp +++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp @@ -55,7 +55,6 @@ #include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h> #include <QtQuick/private/qsgdepthstencilbuffer_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> #endif #include <QtQuick/private/qsggeometry_p.h> #include <QtQuick/private/qsgnode_p.h> diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml new file mode 100644 index 0000000000..b628255a3d --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +SequentialAnimation { + id: tapFlash + running: false + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } +} diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/Slider.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/Slider.qml new file mode 100644 index 0000000000..d01bcf74ed --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/Slider.qml @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + id: root + property int value: 50 + property int maximumValue: 99 + property alias label: label.text + property alias tapEnabled: tap.enabled + property alias pressed: tap.isPressed + signal tapped + + Rectangle { + id: slot + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 10 + anchors.topMargin: 30 + anchors.bottomMargin: 30 + anchors.horizontalCenter: parent.horizontalCenter + width: 10 + color: "black" + radius: width / 2 + smooth: true + } + + Rectangle { + id: glow + anchors.fill: knob + anchors.margins: -5 + anchors.leftMargin: -2 + anchors.horizontalCenterOffset: 1 + radius: 5 + color: "#4400FFFF" + opacity: tap.isPressed || tapFlash.running ? 1 : 0 + FlashAnimation on visible { + id: tapFlash + } + } + Rectangle { + id: knob + objectName: "Slider Knob" + width: parent.width - 2 + height: 20 + radius: 5 + color: "darkgray" + border.color: "black" + property bool programmatic: false + property real multiplier: root.maximumValue / (dragHandler.yAxis.maximum - dragHandler.yAxis.minimum) + onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - dragHandler.yAxis.minimum) * multiplier + transformOrigin: Item.Center + function setValue(value) { knob.y = dragHandler.yAxis.maximum - value / knob.multiplier } + DragHandler { + id: dragHandler + objectName: label.text + " DragHandler" + xAxis.enabled: false + yAxis.minimum: slot.y + yAxis.maximum: slot.height + slot.y - knob.height + } + TapHandler { + id: tap + objectName: label.text + " TapHandler" + gesturePolicy: TapHandler.DragThreshold + onTapped: { + tapFlash.start() + root.tapped + } + } + } + + Text { + font.pointSize: 16 + color: "red" + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: root.value + } + + Text { + id: label + font.pointSize: 12 + color: "red" + anchors.top: parent.top + anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + } + + Component.onCompleted: { + knob.programmatic = true + knob.setValue(root.value) + knob.programmatic = false + } +} diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml new file mode 100644 index 0000000000..4aac89402b --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + property alias label: label.text + property alias pressed: tap.isPressed + property bool checked: false + property alias gesturePolicy: tap.gesturePolicy + property alias enabled: tap.enabled + signal tapped + + width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0 + border.color: "#9f9d9a"; border.width: 1; radius: height / 4; antialiasing: true + + gradient: Gradient { + GradientStop { position: 0.0; color: tap.isPressed ? "#b8b5b2" : "#efebe7" } + GradientStop { position: 1.0; color: "#b8b5b2" } + } + + TapHandler { + id: tap + objectName: label.text + longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped + onTapped: { + tapFlash.start() + root.tapped() + } + } + + Text { + id: label + font.pointSize: 14 + text: "Button" + anchors.centerIn: parent + } + + Rectangle { + anchors.fill: parent; anchors.margins: -5 + color: "transparent"; border.color: "#4400FFFF" + border.width: 5; radius: root.radius; antialiasing: true + opacity: tapFlash.running ? 1 : 0 + FlashAnimation on visible { id: tapFlash } + } + + Rectangle { + objectName: "expandingCircle" + radius: tap.timeHeld * 100 + visible: radius > 0 && tap.isPressed + border.width: 3 + border.color: "cyan" + color: "transparent" + width: radius * 2 + height: radius * 2 + x: tap.point.scenePressPosition.x - radius + y: tap.point.scenePressPosition.y - radius + opacity: 0.25 + Component.onCompleted: { + // get on top of all the buttons + var par = root.parent; + while (par.parent) + par = par.parent; + parent = par; + } + } +} diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml new file mode 100644 index 0000000000..833fef0a81 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + width: 400 + height: 480 + objectName: "root" + color: "#222222" + + Flickable { + anchors.fill: parent + anchors.margins: 10 + anchors.topMargin: 40 + contentHeight: 600 + contentWidth: 600 +// pressDelay: TODO + + Row { + spacing: 6 + Slider { + label: "DragHandler" + objectName: "Slider" + value: 49; width: 100; height: 400 + } + Column { + spacing: 6 + TapHandlerButton { + objectName: "DragThreshold" + label: "DragThreshold" + gesturePolicy: TapHandler.DragThreshold + } + TapHandlerButton { + objectName: "WithinBounds" + label: "WithinBounds" + gesturePolicy: TapHandler.WithinBounds + } + TapHandlerButton { + objectName: "ReleaseWithinBounds" + label: "ReleaseWithinBounds" + gesturePolicy: TapHandler.ReleaseWithinBounds // the default + } + } + } + } +} + diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/flickableinterop.pro b/tests/auto/quick/pointerhandlers/flickableinterop/flickableinterop.pro new file mode 100644 index 0000000000..9075044bd3 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/flickableinterop.pro @@ -0,0 +1,15 @@ +CONFIG += testcase + +TARGET = tst_flickableinterop +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_flickableinterop.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +OTHER_FILES += data/flickableWithHandlers.qml data/Slider.qml diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp new file mode 100644 index 0000000000..c8fe6052fb --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp @@ -0,0 +1,475 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <QtGui/qstylehints.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickflickable_p.h> +#include <QtQuick/private/qquickpointerhandler_p.h> +#include <QtQuick/private/qquickdraghandler_p.h> +#include <QtQuick/private/qquicktaphandler_p.h> +#include <qpa/qwindowsysteminterface.h> + +#include <private/qquickwindow_p.h> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlproperty.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_FlickableInterop : public QQmlDataTest +{ + Q_OBJECT +public: + tst_FlickableInterop() + :touchDevice(QTest::createTouchDevice()) + {} + +private slots: + void initTestCase(); + + void touchTapButton_data(); + void touchTapButton(); + void touchDragFlickableBehindButton_data(); + void touchDragFlickableBehindButton(); + void mouseClickButton_data(); + void mouseClickButton(); + void mouseDragFlickableBehindButton_data(); + void mouseDragFlickableBehindButton(); + void touchDragSlider(); + void touchDragFlickableBehindSlider(); + void mouseDragSlider(); + void mouseDragFlickableBehindSlider(); + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); + QTouchDevice *touchDevice; +}; + +void tst_FlickableInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); +} + +void tst_FlickableInterop::initTestCase() +{ + // This test assumes that we don't get synthesized mouse events from QGuiApplication + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); + + QQmlDataTest::initTestCase(); +} + +void tst_FlickableInterop::touchTapButton_data() +{ + QTest::addColumn<QString>("buttonName"); + QTest::newRow("DragThreshold") << QStringLiteral("DragThreshold"); + QTest::newRow("WithinBounds") << QStringLiteral("WithinBounds"); + QTest::newRow("ReleaseWithinBounds") << QStringLiteral("ReleaseWithinBounds"); +} + +void tst_FlickableInterop::touchTapButton() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QFETCH(QString, buttonName); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(buttonName); + QVERIFY(button); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + // Button changes pressed state and emits tapped on release + QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 1); + + // We can drag <= dragThreshold and the button still acts normal, Flickable doesn't grab + p1 = button->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 2); +} + +void tst_FlickableInterop::touchDragFlickableBehindButton_data() +{ + QTest::addColumn<QString>("buttonName"); + QTest::newRow("DragThreshold") << QStringLiteral("DragThreshold"); + QTest::newRow("WithinBounds") << QStringLiteral("WithinBounds"); + QTest::newRow("ReleaseWithinBounds") << QStringLiteral("ReleaseWithinBounds"); +} + +void tst_FlickableInterop::touchDragFlickableBehindButton() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QFETCH(QString, buttonName); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(buttonName); + QVERIFY(button); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + tappedSpy.clear(); + QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(button->property("pressed").toBool()); + int i = 0; + // Start dragging; eventually when the touchpoint goes beyond dragThreshold, + // Button is no longer pressed because Flickable steals the grab + for (; i < 100 && !flickable->isMoving(); ++i) { + p1 += QPoint(1, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + } + QVERIFY(flickable->isMoving()); + qDebug() << "flickable started moving after" << i << "moves, when we got to" << p1; + QCOMPARE(i, 2); + QVERIFY(!button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 0); +} + +void tst_FlickableInterop::mouseClickButton_data() +{ + QTest::addColumn<QString>("buttonName"); + QTest::newRow("DragThreshold") << QStringLiteral("DragThreshold"); + QTest::newRow("WithinBounds") << QStringLiteral("WithinBounds"); + QTest::newRow("ReleaseWithinBounds") << QStringLiteral("ReleaseWithinBounds"); +} + +void tst_FlickableInterop::mouseClickButton() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QFETCH(QString, buttonName); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(buttonName); + QVERIFY(button); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + // Button changes pressed state and emits tapped on release + QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 1); + + // We can drag <= dragThreshold and the button still acts normal, Flickable doesn't grab + p1 = button->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QVERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 2); +} + +void tst_FlickableInterop::mouseDragFlickableBehindButton_data() +{ + QTest::addColumn<QString>("buttonName"); + QTest::newRow("DragThreshold") << QStringLiteral("DragThreshold"); + QTest::newRow("WithinBounds") << QStringLiteral("WithinBounds"); + QTest::newRow("ReleaseWithinBounds") << QStringLiteral("ReleaseWithinBounds"); +} + +void tst_FlickableInterop::mouseDragFlickableBehindButton() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QFETCH(QString, buttonName); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(buttonName); + QVERIFY(button); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + // Button is no longer pressed if touchpoint goes beyond dragThreshold, + // because Flickable steals the grab + tappedSpy.clear(); + QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QVERIFY(button->property("pressed").toBool()); + int i = 0; + for (; i < 100 && !flickable->isMoving(); ++i) { + p1 += QPoint(1, 0); + QTest::mouseMove(window, p1); + } + qDebug() << "flickable started moving after" << i << "moves, when we got to" << p1; + QVERIFY(flickable->isMoving()); + QCOMPARE(i, 2); + QVERIFY(!button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 0); +} + +void tst_FlickableInterop::touchDragSlider() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *slider = window->rootObject()->findChild<QQuickItem*>("Slider"); + QVERIFY(slider); + QQuickDragHandler *drag = slider->findChild<QQuickDragHandler*>(); + QVERIFY(drag); + QQuickItem *knob = slider->findChild<QQuickItem*>("Slider Knob"); + QVERIFY(knob); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); + QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + + // Drag the slider in the allowed (vertical) direction + tappedSpy.clear(); + QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(slider->property("pressed").toBool()); + p1 += QPoint(0, dragThreshold); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(slider->property("pressed").toBool()); + QCOMPARE(slider->property("value").toInt(), 49); + p1 += QPoint(0, 1); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + p1 += QPoint(0, 10); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(slider->property("value").toInt() < 49); + QVERIFY(!flickable->isMoving()); + QVERIFY(!slider->property("pressed").toBool()); + + // Now that the DragHandler is active, the Flickable will not steal the grab + // even if we move a large distance horizontally + p1 += QPoint(dragThreshold * 2, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!flickable->isMoving()); + + // Release, and do not expect the tapped signal + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(translationChangedSpy.count(), 1); +} + +void tst_FlickableInterop::mouseDragSlider() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *slider = window->rootObject()->findChild<QQuickItem*>("Slider"); + QVERIFY(slider); + QQuickDragHandler *drag = slider->findChild<QQuickDragHandler*>(); + QVERIFY(drag); + QQuickItem *knob = slider->findChild<QQuickItem*>("Slider Knob"); + QVERIFY(knob); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); + QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + + // Drag the slider in the allowed (vertical) direction + tappedSpy.clear(); + QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(slider->property("pressed").toBool()); + p1 += QPoint(0, dragThreshold); + QTest::mouseMove(window, p1); + QVERIFY(slider->property("pressed").toBool()); + QCOMPARE(slider->property("value").toInt(), 49); + p1 += QPoint(0, 1); + QTest::mouseMove(window, p1); + p1 += QPoint(0, 10); + QTest::mouseMove(window, p1); + QVERIFY(slider->property("value").toInt() < 49); + QVERIFY(!flickable->isMoving()); + QVERIFY(!slider->property("pressed").toBool()); + + // Now that the DragHandler is active, the Flickable will not steal the grab + // even if we move a large distance horizontally + p1 += QPoint(dragThreshold * 2, 0); + QTest::mouseMove(window, p1); + QVERIFY(!flickable->isMoving()); + + // Release, and do not expect the tapped signal + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(translationChangedSpy.count(), 1); +} + +void tst_FlickableInterop::touchDragFlickableBehindSlider() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *slider = window->rootObject()->findChild<QQuickItem*>("Slider"); + QVERIFY(slider); + QQuickDragHandler *drag = slider->findChild<QQuickDragHandler*>(); + QVERIFY(drag); + QQuickItem *knob = slider->findChild<QQuickItem*>("Slider Knob"); + QVERIFY(knob); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); + QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + + // Button is no longer pressed if touchpoint goes beyond dragThreshold, + // because Flickable steals the grab + tappedSpy.clear(); + QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(slider->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(slider->property("pressed").toBool()); + int i = 0; + for (; i < 100 && !flickable->isMoving(); ++i) { + p1 += QPoint(1, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + } + qDebug() << "flickable started moving after" << i << "moves, when we got to" << p1; + QVERIFY(flickable->isMoving()); + QCOMPARE(i, 2); + QVERIFY(!slider->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!slider->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(translationChangedSpy.count(), 0); +} + +void tst_FlickableInterop::mouseDragFlickableBehindSlider() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *slider = window->rootObject()->findChild<QQuickItem*>("Slider"); + QVERIFY(slider); + QQuickDragHandler *drag = slider->findChild<QQuickDragHandler*>(); + QVERIFY(drag); + QQuickItem *knob = slider->findChild<QQuickItem*>("Slider Knob"); + QVERIFY(knob); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); + QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + + // Button is no longer pressed if touchpoint goes beyond dragThreshold, + // because Flickable steals the grab + tappedSpy.clear(); + QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(slider->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QQuickTouchUtils::flush(window); + QVERIFY(slider->property("pressed").toBool()); + int i = 0; + for (; i < 100 && !flickable->isMoving(); ++i) { + p1 += QPoint(1, 0); + QTest::mouseMove(window, p1); + } + qDebug() << "flickable started moving after" << i << "moves, when we got to" << p1; + QVERIFY(flickable->isMoving()); + QCOMPARE(i, 2); + QVERIFY(!slider->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(translationChangedSpy.count(), 0); +} + +QTEST_MAIN(tst_FlickableInterop) + +#include "tst_flickableinterop.moc" + diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml new file mode 100644 index 0000000000..d479882d38 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + width: 1024; height: 600 + color: "beige" + objectName: "beige root" + + Rectangle { + id: container + objectName: "container rect" + width: 600 + height: 500 + color: "black" + border.color: pinch3.active ? "red" : "black" + border.width: 3 + antialiasing: true + + MultiPointTouchArea { + id: mpta + anchors.fill: parent + //onGestureStarted: gesture.grab() // in case this is embedded in something that might steal + touchPoints: [ + TouchPoint { property color color: "red" }, + TouchPoint { property color color: "orange" }, + TouchPoint { property color color: "lightsteelblue" }, + TouchPoint { property color color: "green" } + ] } + + Repeater { + model: 4 + + Item { + id: crosshairs + property TouchPoint touchPoint + x: touchPoint.x - width / 2 + y: touchPoint.y - height / 2 + width: 300; height: 300 + visible: touchPoint.pressed + rotation: touchPoint.rotation + + Rectangle { + color: touchPoint.color + anchors.centerIn: parent + width: 2; height: parent.height + antialiasing: true + } + Rectangle { + color: touchPoint.color + anchors.centerIn: parent + width: parent.width; height: 2 + antialiasing: true + } + Component.onCompleted: touchPoint = mpta.touchPoints[index] + } + } + + Item { + objectName: "pinch and drag" + anchors.fill: parent + // In order for PinchHandler to get a chance to take a passive grab, it has to get the touchpoints first. + // In order to get the touchpoints first, it has to be on top of the Z order: i.e. come last in paintOrderChildItems(). + // This is the opposite situation as with filtersChildMouseEvents: e.g. PinchArea would have wanted to be the parent, + // if it even knew that trick (which it doesn't). + PinchHandler { + id: pinch3 + objectName: "3-finger pinch" + target: container + requiredPointCount: 3 + minimumScale: 0.1 + maximumScale: 10 + } + DragHandler { + id: dragHandler + objectName: "DragHandler" + target: container + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/multipointtoucharea_interop.pro b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/multipointtoucharea_interop.pro new file mode 100644 index 0000000000..10d0ff8018 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/multipointtoucharea_interop.pro @@ -0,0 +1,15 @@ +CONFIG += testcase + +TARGET = tst_multipointtoucharea_interop +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_multipointtoucharea_interop.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +OTHER_FILES += data/pinchDragMPTA.qml diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp new file mode 100644 index 0000000000..09a3c36b6d --- /dev/null +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlproperty.h> +#include <QtQuick/private/qquickdraghandler_p.h> +#include <QtQuick/private/qquickmultipointtoucharea_p.h> +#include <QtQuick/private/qquickpinchhandler_p.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_MptaInterop : public QQmlDataTest +{ + Q_OBJECT +public: + tst_MptaInterop() + : touchDevice(QTest::createTouchDevice()) + , touchPointerDevice(QQuickPointerDevice::touchDevice(touchDevice)) + {} + +private slots: + void initTestCase(); + + void touchDrag(); + void touchesThenPinch(); + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); + QTouchDevice *touchDevice; + QQuickPointerDevice *touchPointerDevice; +}; + +void tst_MptaInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); +} + +void tst_MptaInterop::initTestCase() +{ + // This test assumes that we don't get synthesized mouse events from QGuiApplication + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); + + QQmlDataTest::initTestCase(); +} + +void tst_MptaInterop::touchDrag() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "pinchDragMPTA.qml"); + QQuickView * window = windowPtr.data(); + + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(); + QVERIFY(mpta); + QQuickPinchHandler *pinch = window->rootObject()->findChild<QQuickPinchHandler*>(); + QVERIFY(pinch); + QQuickDragHandler *drag = window->rootObject()->findChild<QQuickDragHandler*>(); + QVERIFY(drag); + QQmlListReference tp(mpta, "touchPoints"); + QVERIFY(tp.at(3)); // the QML declares four touchpoints + QSignalSpy mptaPressedSpy(mpta, SIGNAL(pressed(QList<QObject*>))); + QSignalSpy mptaReleasedSpy(mpta, SIGNAL(released(QList<QObject*>))); + QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice); + + // Press one touchpoint: + // DragHandler gets a passive grab + // PinchHandler declines, because it wants 3 touchpoints + // MPTA doesn't get a chance, because DragHandler accepted the single EventPoint + QPoint p1 = mpta->mapToScene(QPointF(20, 20)).toPoint(); + touch.press(1, p1).commit(); + QQuickTouchUtils::flush(window); + auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(touchPointerDevice); + QCOMPARE(tp.at(0)->property("pressed").toBool(), false); +// QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + + // Start moving + // DragHandler gets keeps monitoring, due to its passive grab, + // and eventually steals the exclusive grab from MPTA + int dragStoleGrab = 0; + for (int i = 0; i < 4; ++i) { + p1 += QPoint(dragThreshold / 2, 0); + touch.move(1, p1).commit(); + QQuickTouchUtils::flush(window); + if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == drag) + dragStoleGrab = i; +// QCOMPARE(tp.at(0)->property("pressed").toBool(), !dragStoleGrab); + } + qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab); + QVERIFY(dragStoleGrab > 1); + + touch.release(1, p1).commit(); + QQuickTouchUtils::flush(window); +} + +// TODO touchesThenPinch_data with press/release sequences somehow: vectors of touchpoint IDs? or a string representation? +void tst_MptaInterop::touchesThenPinch() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "pinchDragMPTA.qml"); + QQuickView * window = windowPtr.data(); + + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(); + QVERIFY(mpta); + QQuickPinchHandler *pinch = window->rootObject()->findChild<QQuickPinchHandler*>(); + QVERIFY(pinch); + QQuickDragHandler *drag = window->rootObject()->findChild<QQuickDragHandler*>(); + QVERIFY(drag); + QQmlListReference tp(mpta, "touchPoints"); + QVERIFY(tp.at(3)); // the QML declares four touchpoints + QSignalSpy mptaPressedSpy(mpta, SIGNAL(pressed(QList<QObject*>))); + QSignalSpy mptaReleasedSpy(mpta, SIGNAL(released(QList<QObject*>))); + QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice); + auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(touchPointerDevice); + + // Press one touchpoint: + // DragHandler gets a passive grab + // PinchHandler declines, because it wants 3 touchpoints + // MPTA doesn't get a chance, because DragHandler accepted the single EventPoint + QPoint p1 = mpta->mapToScene(QPointF(20, 20)).toPoint(); + touch.press(1, p1).commit(); + QQuickTouchUtils::flush(window); + QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), nullptr); + QTRY_COMPARE(pointerEvent->point(0)->passiveGrabbers().first(), drag); +// QTRY_VERIFY(tp.at(0)->property("pressed").toBool()); + + // Press a second touchpoint: MPTA grabs it + QPoint p2 = mpta->mapToScene(QPointF(200, 30)).toPoint(); + touch.stationary(1).press(2, p2).commit(); + QQuickTouchUtils::flush(window); + QVERIFY(tp.at(0)->property("pressed").toBool()); + QTRY_VERIFY(tp.at(1)->property("pressed").toBool()); + QCOMPARE(mptaPressedSpy.count(), 1); + + // ATM it's required that when PinchHandler sees the third touchpoint, + // the pre-existing points must have moved far enough to exceed the drag threshold. + // If MPTA is allowed to grab that third point, then PinchHandler won't steal. + // TODO should we change that? make sure that if PH has a passive grab, it always gets updated even though MPTA has the grab? + for (int i = 0; i < 2; ++i) { + p1 += QPoint(dragThreshold, dragThreshold); + p2 += QPoint(dragThreshold, dragThreshold); + touch.move(1, p1).move(2, p2).commit(); + } + + // Press a third touchpoint: PinchHandler grabs, MPTA doesn't + QPoint p3 = mpta->mapToScene(QPointF(110, 200)).toPoint(); + touch.stationary(1).stationary(2).press(3, p3).commit(); + QQuickTouchUtils::flush(window); + QCOMPARE(tp.at(0)->property("pressed").toBool(), true); + QCOMPARE(tp.at(1)->property("pressed").toBool(), true); + QCOMPARE(tp.at(2)->property("pressed").toBool(), false); + QCOMPARE(mptaPressedSpy.count(), 1); + QTRY_COMPARE(pointerEvent->point(2)->exclusiveGrabber(), pinch); + QVERIFY(pinch->active()); + + // Move some more: PinchHandler reacts + for (int i = 0; i < 8; ++i) { + p1 += QPoint(4, 4); + p2 += QPoint(4, 4); + p3 += QPoint(-4, 4); + touch.move(1, p1).move(2, p2).move(3, p3).commit(); +// QTRY_COMPARE(tp.at(0)->property("pressed").toBool(), false); // TODO fails; MPTA doesn't know it lost its grabs +// QCOMPARE(tp.at(1)->property("pressed").toBool(), false); +// QCOMPARE(tp.at(2)->property("pressed").toBool(), false); + } + qCDebug(lcPointerTests) << "scale" << pinch->scale() << "rot" << pinch->rotation(); + QTRY_VERIFY(pinch->rotation() > 10); + QVERIFY(pinch->scale() > 1); + + // Press one more point (pinkie finger) + QPoint p4 = mpta->mapToScene(QPointF(300, 200)).toPoint(); + touch.stationary(1).stationary(2).stationary(3).press(4, p4).commit(); + // MPTA grabs p4 (which is at index 3) +// QTRY_COMPARE(pointerEvent->point(3)->exclusiveGrabber(), mpta); + // PinchHandler wantsPointerEvent declines, because it wants exactly 3 touchpoints, and there are now 4. + // Move some more... MPTA reacts, in spite of not grabbing all the points + for (int i = 0; i < 8; ++i) { + p1 += QPoint(4, 4); + p2 += QPoint(4, 4); + p3 += QPoint(-4, 4); + p4 += QPoint(-4, -4); + touch.move(1, p1).move(2, p2).move(3, p3).move(4, p4).commit(); +// QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), nullptr); +// QCOMPARE(pointerEvent->point(1)->exclusiveGrabber(), nullptr); +// QCOMPARE(pointerEvent->point(2)->exclusiveGrabber(), nullptr); +// QCOMPARE(pointerEvent->point(3)->exclusiveGrabber(), mpta); + QCOMPARE(tp.at(0)->property("pressed").toBool(), true); + QCOMPARE(tp.at(1)->property("pressed").toBool(), true); +// QCOMPARE(tp.at(2)->property("pressed").toBool(), true); +// QCOMPARE(tp.at(3)->property("pressed").toBool(), true); + } + + // Release the pinkie + touch.stationary(1).stationary(2).stationary(3).release(4, p4).commit(); + // Move some more: PinchHander grabs again, and reacts + for (int i = 0; i < 8; ++i) { + p1 -= QPoint(4, 4); + p2 += QPoint(4, 4); + p3 -= QPoint(-4, 4); + touch.move(1, p1).move(2, p2).move(3, p3).commit(); + QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), pinch); + } + + // Release the first finger + touch.stationary(2).stationary(3).release(1, p1).commit(); + // Move some more: PinchHander isn't interested in a mere 2 points, and MPTA should react... but it doesn't (TODO?) + for (int i = 0; i < 8; ++i) { + p1 -= QPoint(4, 4); + p2 += QPoint(4, 4); + touch.move(1, p1).move(2, p2).commit(); + QTest::qWait(100); + } + + touch.release(1, p1).release(2, p2).release(3, p3).commit(); + QQuickTouchUtils::flush(window); +// QTRY_COMPARE(mptaReleasedSpy.count(), 1); // all points at once +} + +QTEST_MAIN(tst_MptaInterop) + +#include "tst_multipointtoucharea_interop.moc" diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro new file mode 100644 index 0000000000..2492924944 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs + +qtConfig(private_tests) { + SUBDIRS += \ + flickableinterop \ + multipointtoucharea_interop \ + qquickpointerhandler \ + qquickdraghandler \ + qquicktaphandler \ +} + diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml new file mode 100644 index 0000000000..9b0ef81635 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + id: root + objectName: label + property int value: 50 + property int maximumValue: 99 + property alias label: label.text + property alias tapEnabled: tap.enabled + property alias pressed: tap.isPressed + signal tapped + width: 140 + height: 400 + + DragHandler { + id: dragHandler + objectName: label.text + " DragHandler" + target: knob + xAxis.enabled: false + yAxis.minimum: slot.y + yAxis.maximum: slot.height + slot.y - knob.height + } + + Rectangle { + id: slot + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 10 + anchors.topMargin: 30 + anchors.bottomMargin: 30 + anchors.horizontalCenter: parent.horizontalCenter + width: 10 + color: "black" + radius: width / 2 + smooth: true + } + + Rectangle { + id: glow + anchors.fill: knob + anchors.margins: -5 + anchors.leftMargin: -2 + anchors.horizontalCenterOffset: 1 + radius: 5 + color: "#4400FFFF" + opacity: tap.isPressed || tapFlash.running ? 1 : 0 + FlashAnimation on visible { + id: tapFlash + } + } + Rectangle { + id: knob + objectName: "Slider Knob" + width: parent.width - 2 + height: 30 + radius: 5 + color: "darkgray" + border.color: "black" + property bool programmatic: false + property real multiplier: root.maximumValue / (dragHandler.yAxis.maximum - dragHandler.yAxis.minimum) + onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - dragHandler.yAxis.minimum) * multiplier + transformOrigin: Item.Center + function setValue(value) { knob.y = dragHandler.yAxis.maximum - value / knob.multiplier } + TapHandler { + id: tap + objectName: label.text + " TapHandler" + gesturePolicy: TapHandler.DragThreshold + onTapped: { + tapFlash.start() + root.tapped + } + } + } + + Text { + font.pointSize: 16 + color: "red" + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: root.value + } + + Text { + id: label + font.pointSize: 12 + color: "red" + anchors.top: parent.top + anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + } + + Component.onCompleted: { + knob.programmatic = true + knob.setValue(root.value) + knob.programmatic = false + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml new file mode 100644 index 0000000000..2224276819 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +SequentialAnimation { + id: tapFlash + running: false + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml new file mode 100644 index 0000000000..81c261fc7a --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + id: root + objectName: label + property int value: 50 + property int maximumValue: 99 + property alias label: label.text + property alias tapEnabled: tap.enabled + property alias pressed: tap.isPressed + signal tapped + width: 140 + height: 400 + + Rectangle { + id: slot + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 10 + anchors.topMargin: 30 + anchors.bottomMargin: 30 + anchors.horizontalCenter: parent.horizontalCenter + width: 10 + color: "black" + radius: width / 2 + smooth: true + } + + Rectangle { + id: glow + anchors.fill: knob + anchors.margins: -5 + anchors.leftMargin: -2 + anchors.horizontalCenterOffset: 1 + radius: 5 + color: "#4400FFFF" + opacity: tap.isPressed || tapFlash.running ? 1 : 0 + FlashAnimation on visible { + id: tapFlash + } + } + Rectangle { + id: knob + objectName: root.label + " Knob" + width: parent.width - 2 + height: 30 + radius: 5 + color: "darkgray" + border.color: "black" + property bool programmatic: false + property real multiplier: root.maximumValue / (dragHandler.yAxis.maximum - dragHandler.yAxis.minimum) + onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - dragHandler.yAxis.minimum) * multiplier + transformOrigin: Item.Center + function setValue(value) { knob.y = dragHandler.yAxis.maximum - value / knob.multiplier } + function flash() { tapFlash.start() } + DragHandler { + id: dragHandler + objectName: label.text + " DragHandler" + xAxis.enabled: false + yAxis.minimum: slot.y + yAxis.maximum: slot.height + slot.y - knob.height + } + TapHandler { + id: tap + objectName: label.text + " TapHandler" + gesturePolicy: TapHandler.DragThreshold + onTapped: { + tapFlash.start() + root.tapped + } + } + } + + Text { + font.pointSize: 16 + color: "red" + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: root.value + } + + Text { + id: label + font.pointSize: 12 + color: "red" + anchors.top: parent.top + anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + } + + Component.onCompleted: { + knob.programmatic = true + knob.setValue(root.value) + knob.programmatic = false + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml new file mode 100644 index 0000000000..5ed9bd1523 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + id: root + objectName: "Draggables" + width: 640 + height: 480 + + Repeater { + model: 2 + + Rectangle { + id: ball + objectName: "Ball " + index + color: dragHandler.active ? "blue" : "lightsteelblue" + width: 80; height: 80; x: 200 + index * 200; y: 200; radius: width / 2 + onParentChanged: console.log(this + " parent " + parent) + + DragHandler { + id: dragHandler + objectName: "DragHandler " + index + } + + Text { + color: "white" + anchors.centerIn: parent + text: dragHandler.point.position.x.toFixed(1) + "," + dragHandler.point.position.y.toFixed(1) + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml new file mode 100644 index 0000000000..bcb16f54cb --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + width: 900 + height: 850 + objectName: "root" + color: "#222222" + + Grid { + objectName: "grid" + anchors.fill: parent + spacing: 10 + columns: 6 + Repeater { + id: top + objectName: "top" + model: 6 + + delegate: Slider { + objectName: label + label: "Drag Knob " + index + width: 140 + } + } + Repeater { + id: bottom + objectName: "bottom" + model: 6 + + delegate: DragAnywhereSlider { + objectName: label + label: "Drag Anywhere " + index + width: 140 + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/reparenting.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/reparenting.qml new file mode 100644 index 0000000000..3545badd86 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/reparenting.qml @@ -0,0 +1,60 @@ +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Grid { + id: root + objectName: "root" + property bool reparentOnDrag: true + width: 200; height: 200 + columns: 3 + spacing: 10 + Repeater { + model: 9 + anchors.fill: parent + Item { + id: gridPlaceholder + objectName: "gridPlaceholder" + index + width: 60 + height: 60 + Rectangle { + id: icon + border.color: "black" + color: "beige" + radius: 3 + width: 60 + height: 60 + onParentChanged :console.log("parent " + parent) + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + DragHandler { + id: dragArea + } + Text { + anchors.centerIn: parent + text: index + "@" + Math.round(icon.x) + "," + Math.round(icon.y) + font.pointSize: 8 + } + states: [ + State { + when: dragArea.dragging + AnchorChanges { + target: icon + anchors.horizontalCenter: undefined + anchors.verticalCenter: undefined + } + ParentChange { + target: root.reparentOnDrag ? icon : null + parent: root + } + PropertyChanges { + target: icon + color: "yellow" + } + } + ] + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro new file mode 100644 index 0000000000..b50fe5ca6f --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro @@ -0,0 +1,15 @@ +CONFIG += testcase + +TARGET = tst_qquickdraghandler +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_qquickdraghandler.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +# OTHER_FILES += data/foo.qml diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp new file mode 100644 index 0000000000..5b59911965 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlproperty.h> +#include <QtQuick/private/qquickdraghandler_p.h> +#include <QtQuick/private/qquickrepeater_p.h> +#include <QtQuick/private/qquicktaphandler_p.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_DragHandler : public QQmlDataTest +{ + Q_OBJECT +public: + tst_DragHandler() + :touchDevice(QTest::createTouchDevice()) + {} + +private slots: + void initTestCase(); + + void defaultPropertyValues(); + void touchDrag(); + void mouseDrag(); + void touchDragMulti(); + void touchDragMultiSliders_data(); + void touchDragMultiSliders(); + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); + QTouchDevice *touchDevice; +}; + +void tst_DragHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); +} + +void tst_DragHandler::initTestCase() +{ + // This test assumes that we don't get synthesized mouse events from QGuiApplication + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); + + QQmlDataTest::initTestCase(); +} + +void tst_DragHandler::defaultPropertyValues() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "draggables.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *ball = window->rootObject()->childItems().first(); + QVERIFY(ball); + QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + + QCOMPARE(dragHandler->acceptedButtons(), Qt::LeftButton); + QCOMPARE(dragHandler->translation(), QPointF()); + QCOMPARE(dragHandler->point().position(), QPointF()); + QCOMPARE(dragHandler->point().scenePosition(), QPointF()); + QCOMPARE(dragHandler->point().pressPosition(), QPointF()); + QCOMPARE(dragHandler->point().scenePressPosition(), QPointF()); + QCOMPARE(dragHandler->point().sceneGrabPosition(), QPointF()); +} + +void tst_DragHandler::touchDrag() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "draggables.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *ball = window->rootObject()->childItems().first(); + QVERIFY(ball); + QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + + QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); + + QPointF ballCenter = ball->clipRect().center(); + QPointF scenePressPos = ball->mapToScene(ballCenter); + QPoint p1 = scenePressPos.toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!dragHandler->active()); + QCOMPARE(dragHandler->point().position(), ballCenter); + QCOMPARE(dragHandler->point().pressPosition(), ballCenter); + QCOMPARE(dragHandler->point().scenePosition(), scenePressPos); + QCOMPARE(dragHandler->point().scenePressPosition(), scenePressPos); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!dragHandler->active()); + p1 += QPoint(1, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(dragHandler->active()); + QCOMPARE(translationChangedSpy.count(), 0); + QCOMPARE(dragHandler->translation().x(), 0.0); + QPointF sceneGrabPos = p1; + QCOMPARE(dragHandler->point().sceneGrabPosition(), sceneGrabPos); + p1 += QPoint(19, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(dragHandler->active()); + QCOMPARE(dragHandler->point().position(), ballCenter); + QCOMPARE(dragHandler->point().pressPosition(), ballCenter); + QCOMPARE(dragHandler->point().scenePosition(), ball->mapToScene(ballCenter)); + QCOMPARE(dragHandler->point().scenePressPosition(), scenePressPos); + QCOMPARE(dragHandler->point().sceneGrabPosition(), sceneGrabPos); + QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler->translation().y(), 0.0); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!dragHandler->active()); + QCOMPARE(dragHandler->point().pressedButtons(), Qt::NoButton); + QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); + QCOMPARE(translationChangedSpy.count(), 1); +} + +void tst_DragHandler::mouseDrag() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "draggables.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *ball = window->rootObject()->childItems().first(); + QVERIFY(ball); + QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + + QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); + + QPointF ballCenter = ball->clipRect().center(); + QPointF scenePressPos = ball->mapToScene(ballCenter); + QPoint p1 = scenePressPos.toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!dragHandler->active()); + QCOMPARE(dragHandler->point().position(), ballCenter); + QCOMPARE(dragHandler->point().pressPosition(), ballCenter); + QCOMPARE(dragHandler->point().scenePosition(), scenePressPos); + QCOMPARE(dragHandler->point().scenePressPosition(), scenePressPos); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QVERIFY(!dragHandler->active()); + p1 += QPoint(1, 0); + QTest::mouseMove(window, p1); + QTRY_VERIFY(dragHandler->active()); + QCOMPARE(translationChangedSpy.count(), 0); + QCOMPARE(dragHandler->translation().x(), 0.0); + QPointF sceneGrabPos = p1; + QCOMPARE(dragHandler->point().sceneGrabPosition(), sceneGrabPos); + p1 += QPoint(19, 0); + QTest::mouseMove(window, p1); + QTRY_VERIFY(dragHandler->active()); + QCOMPARE(dragHandler->point().position(), ballCenter); + QCOMPARE(dragHandler->point().pressPosition(), ballCenter); + QCOMPARE(dragHandler->point().scenePosition(), ball->mapToScene(ballCenter)); + QCOMPARE(dragHandler->point().scenePressPosition(), scenePressPos); + QCOMPARE(dragHandler->point().sceneGrabPosition(), sceneGrabPos); + QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler->translation().y(), 0.0); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!dragHandler->active()); + QCOMPARE(dragHandler->point().pressedButtons(), Qt::NoButton); + QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); + QCOMPARE(translationChangedSpy.count(), 1); +} + +void tst_DragHandler::touchDragMulti() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "draggables.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *ball1 = window->rootObject()->childItems().first(); + QVERIFY(ball1); + QQuickDragHandler *dragHandler1 = ball1->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler1); + QSignalSpy translationChangedSpy1(dragHandler1, SIGNAL(translationChanged())); + + QQuickItem *ball2 = window->rootObject()->childItems().at(1); + QVERIFY(ball2); + QQuickDragHandler *dragHandler2 = ball2->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler2); + QSignalSpy translationChangedSpy2(dragHandler2, SIGNAL(translationChanged())); + + QPointF ball1Center = ball1->clipRect().center(); + QPointF scenePressPos1 = ball1->mapToScene(ball1Center); + QPoint p1 = scenePressPos1.toPoint(); + QPointF ball2Center = ball2->clipRect().center(); + QPointF scenePressPos2 = ball2->mapToScene(ball2Center); + QPoint p2 = scenePressPos2.toPoint(); + + QTest::touchEvent(window, touchDevice).press(1, p1, window).press(2, p2, window); + QQuickTouchUtils::flush(window); + QVERIFY(!dragHandler1->active()); + QCOMPARE(dragHandler1->point().position(), ball1Center); + QCOMPARE(dragHandler1->point().pressPosition(), ball1Center); + QCOMPARE(dragHandler1->point().scenePosition(), scenePressPos1); + QCOMPARE(dragHandler1->point().scenePressPosition(), scenePressPos1); + QVERIFY(!dragHandler2->active()); + QCOMPARE(dragHandler2->point().position(), ball2Center); + QCOMPARE(dragHandler2->point().pressPosition(), ball2Center); + QCOMPARE(dragHandler2->point().scenePosition(), scenePressPos2); + QCOMPARE(dragHandler2->point().scenePressPosition(), scenePressPos2); + p1 += QPoint(dragThreshold, 0); + p2 += QPoint(0, dragThreshold); + QTest::touchEvent(window, touchDevice).move(1, p1, window).move(2, p2, window); + QQuickTouchUtils::flush(window); + QVERIFY(!dragHandler1->active()); + p1 += QPoint(1, 0); + p2 += QPoint(0, 1); + QTest::touchEvent(window, touchDevice).move(1, p1, window).move(2, p2, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(dragHandler1->active()); + QVERIFY(dragHandler2->active()); + QCOMPARE(translationChangedSpy1.count(), 0); + QCOMPARE(dragHandler1->translation().x(), 0.0); + QPointF sceneGrabPos1 = p1; + QPointF sceneGrabPos2 = p2; + QCOMPARE(dragHandler1->point().sceneGrabPosition(), sceneGrabPos1); + QCOMPARE(dragHandler2->point().sceneGrabPosition(), sceneGrabPos2); + p1 += QPoint(19, 0); + p2 += QPoint(0, 19); + QVERIFY(dragHandler2->active()); + QCOMPARE(translationChangedSpy2.count(), 0); + QCOMPARE(dragHandler2->translation().x(), 0.0); + QCOMPARE(dragHandler2->point().sceneGrabPosition(), sceneGrabPos2); + QTest::touchEvent(window, touchDevice).move(1, p1, window).move(2, p2, window); + QQuickTouchUtils::flush(window); + QVERIFY(dragHandler1->active()); + QVERIFY(dragHandler2->active()); + QCOMPARE(dragHandler1->point().position(), ball1Center); + QCOMPARE(dragHandler1->point().pressPosition(), ball1Center); + QCOMPARE(dragHandler1->point().scenePosition(), ball1->mapToScene(ball1Center)); + QCOMPARE(dragHandler1->point().scenePressPosition(), scenePressPos1); + QCOMPARE(dragHandler1->point().sceneGrabPosition(), sceneGrabPos1); + QCOMPARE(dragHandler1->translation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler1->translation().y(), 0.0); + QCOMPARE(dragHandler2->point().position(), ball2Center); + QCOMPARE(dragHandler2->point().pressPosition(), ball2Center); + QCOMPARE(dragHandler2->point().scenePosition(), ball2->mapToScene(ball2Center)); + QCOMPARE(dragHandler2->point().scenePressPosition(), scenePressPos2); + QCOMPARE(dragHandler2->point().sceneGrabPosition(), sceneGrabPos2); + QCOMPARE(dragHandler2->translation().x(), 0.0); + QCOMPARE(dragHandler2->translation().y(), dragThreshold + 20.0); + QTest::touchEvent(window, touchDevice).release(1, p1, window).stationary(2); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!dragHandler1->active()); + QVERIFY(dragHandler2->active()); + QCOMPARE(dragHandler1->point().pressedButtons(), Qt::NoButton); + QCOMPARE(ball1->mapToScene(ball1Center).toPoint(), p1); + QCOMPARE(translationChangedSpy1.count(), 1); + QTest::touchEvent(window, touchDevice).release(2, p2, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!dragHandler2->active()); + QCOMPARE(ball2->mapToScene(ball2Center).toPoint(), p2); + QCOMPARE(translationChangedSpy2.count(), 1); +} + +void tst_DragHandler::touchDragMultiSliders_data() +{ + QTest::addColumn<int>("sliderRow"); + QTest::addColumn<QVector<int> >("whichSliders"); + QTest::addColumn<QVector<int> >("startingCenterOffsets"); + QTest::addColumn<QVector<QVector2D> >("movements"); + + QTest::newRow("Drag Knob: start on the knobs, drag down") << + 0 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; + QTest::newRow("Drag Knob: start on the knobs, drag diagonally downward") << + 0 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; + // TOOD these fail +// QTest::newRow("Drag Anywhere: start on the knobs, drag down") << +// 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; +// QTest::newRow("Drag Anywhere: start on the knobs, drag diagonally downward") << +// 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; + // TODO these next two fail because the DragHandler grabs when a finger + // drags across it from outside, but should rather start only if it is pressed inside +// QTest::newRow("Drag Knob: start above the knobs, drag down") << +// 0 << QVector<int> { 0, 1, 2 } << QVector<int> { -30, -30, -30 } << QVector<QVector2D> { {0, 40}, {0, 60}, {0, 80} }; +// QTest::newRow("Drag Knob: start above the knobs, drag diagonally downward") << +// 0 << QVector<int> { 0, 1, 2 } << QVector<int> { -30, -30, -30 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; + QTest::newRow("Drag Anywhere: start above the knobs, drag down") << + 1 << QVector<int> { 0, 1, 2 } << QVector<int> { -20, -30, -40 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; + QTest::newRow("Drag Anywhere: start above the knobs, drag diagonally downward") << + 1 << QVector<int> { 0, 1, 2 } << QVector<int> { -20, -30, -40 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; +} + +void tst_DragHandler::touchDragMultiSliders() +{ + QFETCH(int, sliderRow); + QFETCH(QVector<int>, whichSliders); + QFETCH(QVector<int>, startingCenterOffsets); + QFETCH(QVector<QVector2D>, movements); + const int moveCount = 8; + + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "multipleSliders.qml"); + QQuickView * window = windowPtr.data(); + QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice); + + QQuickRepeater *rowRepeater = window->rootObject()->findChildren<QQuickRepeater *>()[sliderRow]; + QVector<QQuickItem *> knobs; + QVector<QQuickDragHandler *> dragHandlers; + QVector<QQuickTapHandler *> tapHandlers; + QVector<QPointF> startPoints; + for (int sli : whichSliders) { + QQuickItem *slider = rowRepeater->itemAt(sli); + QVERIFY(slider); + dragHandlers << slider->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandlers[sli]); + tapHandlers << slider->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandlers[sli]); + knobs << tapHandlers[sli]->parentItem(); + QPointF startPoint = knobs[sli]->mapToScene(knobs[sli]->clipRect().center()); + startPoint.setY(startPoint.y() + startingCenterOffsets[sli]); + startPoints << startPoint; + qCDebug(lcPointerTests) << "row" << sliderRow << "slider" << sli << slider->objectName() << + "start" << startingCenterOffsets[sli] << startPoints[sli]; + } + QVector<QPointF> touchPoints = startPoints; + + // Press + for (int sli : whichSliders) + touch.press(sli, touchPoints[sli].toPoint()); + touch.commit(); + + // Moves + for (int m = 0; m < moveCount; ++m) { + for (int sli : whichSliders) { + QVector2D incr = movements[sli] / moveCount; + touchPoints[sli] += incr.toPointF(); + touch.move(sli, touchPoints[sli].toPoint()); + } + touch.commit(); + QQuickTouchUtils::flush(window); + } + + // Check that they moved to where they should: since the slider is constrained, + // only the y component should have an effect; knobs should not come out of their "grooves" + for (int sli : whichSliders) { + QPoint endPosition = knobs[sli]->mapToScene(knobs[sli]->clipRect().center()).toPoint(); + QPoint expectedEndPosition(startPoints[sli].x(), startPoints[sli].y() + movements[sli].y()); + if (sliderRow == 0 && qAbs(startingCenterOffsets[sli]) > knobs[sli]->height() / 2) + expectedEndPosition = startPoints[sli].toPoint(); + qCDebug(lcPointerTests) << "slider " << knobs[sli]->objectName() << "started @" << startPoints[sli] + << "tried to move by" << movements[sli] << "ended up @" << endPosition << "expected" << expectedEndPosition; + QTRY_COMPARE(endPosition, expectedEndPosition); + } + + // Release + for (int sli : whichSliders) + touch.release(sli, touchPoints[sli].toPoint()); + touch.commit(); +} + +QTEST_MAIN(tst_DragHandler) + +#include "tst_qquickdraghandler.moc" + diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/singleitem.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/singleitem.qml new file mode 100644 index 0000000000..126cf3ff2b --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/singleitem.qml @@ -0,0 +1,28 @@ +import QtQuick 2.8 +import Qt.test 1.0 + +Item { + id: root + objectName: "root Item" + width: 320 + height: 480 + + Rectangle { + objectName: "eventItem's bounds" + anchors.fill: eventItem + color: "lightsteelblue" + } + + EventItem { + id: eventItem + objectName: "eventItem1" + x: 5 + y: 5 + height: 30 + width: 30 + + EventHandler { + objectName: "eventHandler" + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/qquickpointerhandler.pro b/tests/auto/quick/pointerhandlers/qquickpointerhandler/qquickpointerhandler.pro new file mode 100644 index 0000000000..c386969206 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/qquickpointerhandler.pro @@ -0,0 +1,16 @@ +CONFIG += testcase + +TARGET = tst_qquickpointerhandler +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_qquickpointerhandler.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +# OTHER_FILES += data/foo.qml + diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp new file mode 100644 index 0000000000..bb4361255c --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -0,0 +1,579 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <private/qdebug_p.h> +#include <QtGui/qstylehints.h> +#include <QtQuick/private/qquickpointerhandler_p.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class Event +{ + Q_GADGET +public: + enum Destination { + FilterDestination, + MouseDestination, + TouchDestination, + HandlerDestination + }; + Q_ENUM(Destination) + + Event(Destination d, QEvent::Type t, Qt::TouchPointState s, int grabState, QPointF item, QPointF scene) + : destination(d), type(t), state(s), grabState(grabState), posWrtItem(item), posWrtScene(scene) + {} + + Destination destination; + QEvent::Type type; // if this represents a QEvent that was received + Qt::TouchPointState state; // if this represents an event (pointer, touch or mouse) + int grabState; // if this represents an onGrabChanged() notification (QQuickEventPoint::GrabState) + QPointF posWrtItem; + QPointF posWrtScene; +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const class Event &event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "Event("; + QtDebugUtils::formatQEnum(dbg, event.destination); + dbg << ' '; + QtDebugUtils::formatQEnum(dbg, event.type); + dbg << ' '; + QtDebugUtils::formatQEnum(dbg, event.state); + if (event.grabState) { + dbg << ' '; + QtDebugUtils::formatQEnum(dbg, QQuickEventPoint::GrabState(event.grabState)); + } + dbg << " @ "; + QtDebugUtils::formatQPoint(dbg, event.posWrtItem); + dbg << " S "; + QtDebugUtils::formatQPoint(dbg, event.posWrtScene); + dbg << ')'; + return dbg; +} +#endif + +enum { + NoGrab = 0, +}; + +class EventItem : public QQuickItem +{ + Q_OBJECT +public: + EventItem(QQuickItem *parent = 0) + : QQuickItem(parent), acceptPointer(false), grabPointer(false), acceptMouse(false), acceptTouch(false), filterTouch(false) + {} + + inline int grabState(bool accept, Qt::TouchPointState state) { + return (accept && (state != Qt::TouchPointReleased)) ? (int)QQuickEventPoint::GrabExclusive : (int)NoGrab; + } + + void touchEvent(QTouchEvent *event) + { + qCDebug(lcPointerTests) << event << "will accept?" << acceptTouch; + for (const QTouchEvent::TouchPoint &tp : event->touchPoints()) + eventList.append(Event(Event::TouchDestination, event->type(), tp.state(), grabState(acceptTouch, tp.state()), tp.pos(), tp.scenePos())); + event->setAccepted(acceptTouch); + } + void mousePressEvent(QMouseEvent *event) + { + qCDebug(lcPointerTests) << event; + eventList.append(Event(Event::MouseDestination, event->type(), Qt::TouchPointPressed, grabState(acceptMouse, Qt::TouchPointPressed), event->pos(), event->windowPos())); + event->setAccepted(acceptMouse); + } + void mouseMoveEvent(QMouseEvent *event) + { + qCDebug(lcPointerTests) << event; + eventList.append(Event(Event::MouseDestination, event->type(), Qt::TouchPointMoved, grabState(acceptMouse, Qt::TouchPointMoved), event->pos(), event->windowPos())); + event->setAccepted(acceptMouse); + } + void mouseReleaseEvent(QMouseEvent *event) + { + qCDebug(lcPointerTests) << event; + eventList.append(Event(Event::MouseDestination, event->type(), Qt::TouchPointReleased, grabState(acceptMouse, Qt::TouchPointReleased), event->pos(), event->windowPos())); + event->setAccepted(acceptMouse); + } + void mouseDoubleClickEvent(QMouseEvent *event) + { + qCDebug(lcPointerTests) << event; + eventList.append(Event(Event::MouseDestination, event->type(), Qt::TouchPointPressed, grabState(acceptMouse, Qt::TouchPointPressed), event->pos(), event->windowPos())); + event->setAccepted(acceptMouse); + } + + void mouseUngrabEvent() + { + qCDebug(lcPointerTests); + eventList.append(Event(Event::MouseDestination, QEvent::UngrabMouse, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive, QPoint(0,0), QPoint(0,0))); + } + + bool event(QEvent *event) + { + qCDebug(lcPointerTests) << event; + return QQuickItem::event(event); + } + + QList<Event> eventList; + bool acceptPointer; + bool grabPointer; + bool acceptMouse; + bool acceptTouch; + bool filterTouch; // when used as event filter + + bool eventFilter(QObject *o, QEvent *event) + { + qCDebug(lcPointerTests) << event << o; + if (event->type() == QEvent::TouchBegin || + event->type() == QEvent::TouchUpdate || + event->type() == QEvent::TouchCancel || + event->type() == QEvent::TouchEnd) { + QTouchEvent *touch = static_cast<QTouchEvent*>(event); + for (const QTouchEvent::TouchPoint &tp : touch->touchPoints()) + eventList.append(Event(Event::FilterDestination, event->type(), tp.state(), QQuickEventPoint::GrabExclusive, tp.pos(), tp.scenePos())); + if (filterTouch) + event->accept(); + return true; + } + return false; + } +}; + +#define QCOMPARE_EVENT(i, d, t, s, g) \ + {\ + const Event &event = eventItem1->eventList.at(i);\ + QCOMPARE(event.destination, d);\ + QCOMPARE(event.type, t);\ + QCOMPARE(event.state, s);\ + QCOMPARE(event.grabState, g);\ + }\ + +class EventHandler : public QQuickPointerHandler +{ + void handlePointerEventImpl(QQuickPointerEvent *event) override + { + QQuickPointerHandler::handlePointerEventImpl(event); + if (!enabled()) + return; + EventItem *item = static_cast<EventItem *>(target()); + qCDebug(lcPointerTests) << item->objectName() << event; + int c = event->pointCount(); + for (int i = 0; i < c; ++i) { + QQuickEventPoint *point = event->point(i); + if (item->acceptPointer) + point->setAccepted(item->acceptPointer); // does NOT imply a grab + if (item->grabPointer) + setExclusiveGrab(point, true); + qCDebug(lcPointerTests) << " " << i << ":" << point << "accepted?" << item->acceptPointer << "grabbed?" << (point->exclusiveGrabber() == this); + item->eventList.append(Event(Event::HandlerDestination, QEvent::Pointer, + static_cast<Qt::TouchPointState>(point->state()), + item->grabPointer ? (int)QQuickEventPoint::GrabExclusive : (int)NoGrab, + eventPos(point), point->scenePos())); + } + } + + void onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override + { + EventItem *item = static_cast<EventItem *>(target()); + item->eventList.append(Event(Event::HandlerDestination, QEvent::None, + static_cast<Qt::TouchPointState>(point->state()), stateChange, eventPos(point), point->scenePos())); + } +}; + +class tst_PointerHandlers : public QQmlDataTest +{ + Q_OBJECT +public: + tst_PointerHandlers() + :touchDevice(QTest::createTouchDevice()) + {} + +private slots: + void initTestCase(); + + void touchEventDelivery(); + void mouseEventDelivery(); + void touchReleaseOutside_data(); + void touchReleaseOutside(); + +protected: + bool eventFilter(QObject *, QEvent *event) + { + Qt::TouchPointState tpState; + switch (event->type()) { + case QEvent::MouseButtonPress: + tpState = Qt::TouchPointPressed; + break; + case QEvent::MouseMove: + tpState = Qt::TouchPointMoved; + break; + case QEvent::MouseButtonRelease: + tpState = Qt::TouchPointReleased; + break; + default: + // So far we aren't recording filtered touch events here - they would be quite numerous in some cases + return false; + } + QMouseEvent *me = static_cast<QMouseEvent*>(event); + filteredEventList.append(Event(Event::FilterDestination, event->type(), tpState, + 0, me->pos(), me->globalPos())); + return false; + } + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); + QTouchDevice *touchDevice; + QList<Event> filteredEventList; +}; + +void tst_PointerHandlers::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); +// window->setGeometry(0,0,240,320); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); +} + +void tst_PointerHandlers::initTestCase() +{ + // This test assumes that we don't get synthesized mouse events from QGuiApplication + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); + + QQmlDataTest::initTestCase(); + qmlRegisterType<EventItem>("Qt.test", 1, 0, "EventItem"); + qmlRegisterType<EventHandler>("Qt.test", 1, 0, "EventHandler"); +} + +void tst_PointerHandlers::touchEventDelivery() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "singleitem.qml"); + QQuickView * window = windowPtr.data(); + + EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1"); + QVERIFY(eventItem1); + + // Do not accept anything + QPoint p1 = QPoint(20, 20); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + QTRY_COMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, NoGrab); + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 4); + QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 5); + QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); + eventItem1->eventList.clear(); + + // Accept touch + eventItem1->acceptTouch = true; + p1 = QPoint(20, 20); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(QQuickPointerDevice::touchDevices().at(0)); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), eventItem1); + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 4); + QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); + QCOMPARE_EVENT(3, Event::TouchDestination, QEvent::TouchUpdate, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 6); + QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); + QCOMPARE_EVENT(5, Event::TouchDestination, QEvent::TouchEnd, Qt::TouchPointReleased, NoGrab); + eventItem1->eventList.clear(); + + // wait to avoid getting a double click event + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); + + // Accept mouse + eventItem1->acceptTouch = false; + eventItem1->acceptMouse = true; + eventItem1->setAcceptedMouseButtons(Qt::LeftButton); + p1 = QPoint(20, 20); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + QCOMPARE(window->mouseGrabberItem(), eventItem1); + + QPointF localPos = eventItem1->mapFromScene(p1); + QPointF scenePos = p1; // item is at 0,0 + QCOMPARE(eventItem1->eventList.at(1).posWrtItem, localPos); + QCOMPARE(eventItem1->eventList.at(1).posWrtScene, scenePos); + QCOMPARE(eventItem1->eventList.at(2).posWrtItem, localPos); + QCOMPARE(eventItem1->eventList.at(2).posWrtScene, scenePos); + + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 6); + QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); + QCOMPARE_EVENT(4, Event::TouchDestination, QEvent::TouchUpdate, Qt::TouchPointMoved, NoGrab); + QCOMPARE_EVENT(5, Event::MouseDestination, QEvent::MouseMove, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 10); + QCOMPARE_EVENT(6, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); + QCOMPARE_EVENT(7, Event::TouchDestination, QEvent::TouchEnd, Qt::TouchPointReleased, NoGrab); + QCOMPARE_EVENT(8, Event::MouseDestination, QEvent::MouseButtonRelease, Qt::TouchPointReleased, NoGrab); + QCOMPARE_EVENT(9, Event::MouseDestination, QEvent::UngrabMouse, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); + eventItem1->eventList.clear(); + + // wait to avoid getting a double click event + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); + + // Accept mouse buttons but not the touch event + eventItem1->acceptTouch = false; + eventItem1->acceptMouse = false; + eventItem1->setAcceptedMouseButtons(Qt::LeftButton); + p1 = QPoint(20, 20); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, NoGrab); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), nullptr); + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 4); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 5); + eventItem1->eventList.clear(); + + // wait to avoid getting a double click event + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); + + // Accept touch + eventItem1->acceptTouch = true; + eventItem1->setAcceptedMouseButtons(Qt::LeftButton); + p1 = QPoint(20, 20); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 4); + QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); + QCOMPARE_EVENT(3, Event::TouchDestination, QEvent::TouchUpdate, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 6); + QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); + QCOMPARE_EVENT(5, Event::TouchDestination, QEvent::TouchEnd, Qt::TouchPointReleased, NoGrab); + eventItem1->eventList.clear(); + + // Accept pointer events + eventItem1->acceptPointer = true; + eventItem1->grabPointer = true; + p1 = QPoint(20, 20); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::None, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(eventItem1->eventList.size(), 5); + qCDebug(lcPointerTests) << eventItem1->eventList; + QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, QQuickEventPoint::GrabExclusive); + QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::None, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); + eventItem1->eventList.clear(); +} + +void tst_PointerHandlers::mouseEventDelivery() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "singleitem.qml"); + QQuickView * window = windowPtr.data(); + + EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1"); + QVERIFY(eventItem1); + + // Do not accept anything + QPoint p1 = QPoint(20, 20); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(eventItem1->eventList.size(), 2); + p1 += QPoint(10, 0); + QTest::mouseMove(window, p1); + QCOMPARE(eventItem1->eventList.size(), 3); + QTest::mouseRelease(window, Qt::LeftButton); + QCOMPARE(eventItem1->eventList.size(), 3); + eventItem1->eventList.clear(); + + // wait to avoid getting a double click event + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); + + // Accept mouse + eventItem1->acceptTouch = false; + eventItem1->acceptMouse = true; + eventItem1->setAcceptedMouseButtons(Qt::LeftButton); + p1 = QPoint(20, 20); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); + QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + QCOMPARE(window->mouseGrabberItem(), eventItem1); + + QPointF localPos = eventItem1->mapFromScene(p1); + QPointF scenePos = p1; // item is at 0,0 + QCOMPARE(eventItem1->eventList.at(0).posWrtItem, localPos); + QCOMPARE(eventItem1->eventList.at(0).posWrtScene, scenePos); + QCOMPARE(eventItem1->eventList.at(1).posWrtItem, localPos); + QCOMPARE(eventItem1->eventList.at(1).posWrtScene, scenePos); + + p1 += QPoint(10, 0); + QTest::mouseMove(window, p1); + QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseMove, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(eventItem1->eventList.size(), 5); + QCOMPARE_EVENT(3, Event::MouseDestination, QEvent::MouseButtonRelease, Qt::TouchPointReleased, NoGrab); + QCOMPARE_EVENT(4, Event::MouseDestination, QEvent::UngrabMouse, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); + eventItem1->eventList.clear(); + + // wait to avoid getting a double click event + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); + + // Grab pointer events + eventItem1->acceptMouse = false; + eventItem1->acceptPointer = true; + eventItem1->grabPointer = true; + p1 = QPoint(20, 20); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_COMPARE(eventItem1->eventList.size(), 2); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::None, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + p1 += QPoint(10, 0); + QTest::mouseMove(window, p1); + QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(eventItem1->eventList.size(), 5); + QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, QQuickEventPoint::GrabExclusive); + QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::None, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); + eventItem1->eventList.clear(); +} + +void tst_PointerHandlers::touchReleaseOutside_data() +{ + QTest::addColumn<bool>("acceptPointer"); + QTest::addColumn<bool>("grabPointer"); + QTest::addColumn<int>("eventCount"); + QTest::addColumn<int>("endIndexToTest"); + QTest::addColumn<int>("endDestination"); // Event::Destination + QTest::addColumn<int>("endType"); // QEvent::Type + QTest::addColumn<int>("endState"); // Qt::TouchPointState + QTest::addColumn<int>("endGrabState"); // Qt::TouchPointState + + QTest::newRow("reject and ignore") << false << false << 6 << 5 << (int)Event::TouchDestination + << (int)QEvent::TouchEnd << (int)Qt::TouchPointReleased << (int)NoGrab; + QTest::newRow("reject and grab") << false << true << 5 << 4 << (int)Event::HandlerDestination + << (int)QEvent::None << (int)Qt::TouchPointReleased << (int)QQuickEventPoint::UngrabExclusive; + QTest::newRow("accept and ignore") << true << false << 1 << 0 << (int)Event::HandlerDestination + << (int)QEvent::Pointer << (int)Qt::TouchPointPressed << (int)NoGrab; + QTest::newRow("accept and grab") << true << true << 5 << 4 << (int)Event::HandlerDestination + << (int)QEvent::None << (int)Qt::TouchPointReleased << (int)QQuickEventPoint::UngrabExclusive; +} + +void tst_PointerHandlers::touchReleaseOutside() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "singleitem.qml"); + QQuickView * window = windowPtr.data(); + + QFETCH(bool, acceptPointer); + QFETCH(bool, grabPointer); + QFETCH(int, eventCount); + QFETCH(int, endIndexToTest); + QFETCH(int, endDestination); + QFETCH(int, endType); + QFETCH(int, endState); + QFETCH(int, endGrabState); + + EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1"); + QVERIFY(eventItem1); + + eventItem1->acceptTouch = true; + eventItem1->acceptPointer = acceptPointer; + eventItem1->grabPointer = grabPointer; + + QPoint p1 = QPoint(20, 20); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + p1.setX(eventItem1->mapToScene(eventItem1->clipRect().bottomRight()).x() + 10); + QTest::touchEvent(window, touchDevice).move(0, p1, window); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + QQuickTouchUtils::flush(window); + qCDebug(lcPointerTests) << eventItem1->eventList; + QCOMPARE(eventItem1->eventList.size(), eventCount); + QCOMPARE_EVENT(endIndexToTest, endDestination, endType, endState, endGrabState); +} + +QTEST_MAIN(tst_PointerHandlers) + +#include "tst_qquickpointerhandler.moc" + diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml new file mode 100644 index 0000000000..6203a6769d --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + property alias label: label.text + property alias pressed: tap.isPressed + property bool checked: false + property alias gesturePolicy: tap.gesturePolicy + signal tapped + + width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0 + border.color: "#9f9d9a"; border.width: 1; radius: height / 4; antialiasing: true + + gradient: Gradient { + GradientStop { position: 0.0; color: tap.isPressed ? "#b8b5b2" : "#efebe7" } + GradientStop { position: 1.0; color: "#b8b5b2" } + } + + TapHandler { + id: tap + objectName: label.text + longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped + onTapped: { + tapFlash.start() + root.tapped() + } + } + + Text { + id: label + font.pointSize: 14 + text: "Button" + anchors.centerIn: parent + } + + Rectangle { + anchors.fill: parent + color: "transparent" + border.width: 2; radius: root.radius; antialiasing: true + opacity: tapFlash.running ? 1 : 0 + FlashAnimation on visible { id: tapFlash } + } + + Rectangle { + objectName: "expandingCircle" + radius: tap.timeHeld * 100 + visible: radius > 0 && tap.isPressed + border.width: 3 + border.color: "blue" + color: "transparent" + width: radius * 2 + height: radius * 2 + x: tap.point.scenePressPosition.x - radius + y: tap.point.scenePressPosition.y - radius + opacity: 0.25 + Component.onCompleted: parent = root.parent + } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml new file mode 100644 index 0000000000..2224276819 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +SequentialAnimation { + id: tapFlash + running: false + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml new file mode 100644 index 0000000000..ca1aba71fb --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import Qt.labs.handlers 1.0 + +Item { + width: 320 + height: 240 + Button { + objectName: "DragThreshold" + label: "DragThreshold" + x: 10; y: 10; width: parent.width - 20; height: 40 + gesturePolicy: TapHandler.DragThreshold + } + Button { + objectName: "WithinBounds" + label: "WithinBounds" + x: 10; y: 60; width: parent.width - 20; height: 40 + gesturePolicy: TapHandler.WithinBounds + } + Button { + objectName: "ReleaseWithinBounds" + label: "ReleaseWithinBounds" + x: 10; y: 110; width: parent.width - 20; height: 40 + gesturePolicy: TapHandler.ReleaseWithinBounds + } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/qquicktaphandler.pro b/tests/auto/quick/pointerhandlers/qquicktaphandler/qquicktaphandler.pro new file mode 100644 index 0000000000..b41a94b55e --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/qquicktaphandler.pro @@ -0,0 +1,16 @@ +CONFIG += testcase + +TARGET = tst_qquicktaphandler +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_qquicktaphandler.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +# OTHER_FILES += data/foo.qml + diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp new file mode 100644 index 0000000000..d7eda5e19c --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -0,0 +1,591 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <QtGui/qstylehints.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickpointerhandler_p.h> +#include <QtQuick/private/qquicktaphandler_p.h> +#include <qpa/qwindowsysteminterface.h> + +#include <private/qquickwindow_p.h> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlproperty.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_TapHandler : public QQmlDataTest +{ + Q_OBJECT +public: + tst_TapHandler() + :touchDevice(QTest::createTouchDevice()) + {} + +private slots: + void initTestCase(); + + void touchGesturePolicyDragThreshold(); + void mouseGesturePolicyDragThreshold(); + void touchGesturePolicyWithinBounds(); + void mouseGesturePolicyWithinBounds(); + void touchGesturePolicyReleaseWithinBounds(); + void mouseGesturePolicyReleaseWithinBounds(); + void touchMultiTap(); + void mouseMultiTap(); + void touchLongPress(); + void mouseLongPress(); + void buttonsMultiTouch(); + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); + QTouchDevice *touchDevice; +}; + +void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); +} + +void tst_TapHandler::initTestCase() +{ + // This test assumes that we don't get synthesized mouse events from QGuiApplication + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); + + QQmlDataTest::initTestCase(); +} + +void tst_TapHandler::touchGesturePolicyDragThreshold() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(buttonDragThreshold); + QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); + + // DragThreshold button stays pressed while touchpoint stays within dragThreshold, emits tapped on release + QPoint p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + + // DragThreshold button is no longer pressed if touchpoint goes beyond dragThreshold + dragThresholdTappedSpy.clear(); + p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(1, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 0); +} + +void tst_TapHandler::mouseGesturePolicyDragThreshold() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(buttonDragThreshold); + QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); + + // DragThreshold button stays pressed while mouse stays within dragThreshold, emits tapped on release + QPoint p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QTRY_COMPARE(dragThresholdTappedSpy.count(), 1); + + // DragThreshold button is no longer pressed if mouse goes beyond dragThreshold + dragThresholdTappedSpy.clear(); + p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(1, 0); + QTest::mouseMove(window, p1); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 0); +} + +void tst_TapHandler::touchGesturePolicyWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>("WithinBounds"); + QVERIFY(buttonWithinBounds); + QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); + + // WithinBounds button stays pressed while touchpoint stays within bounds, emits tapped on release + QPoint p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + + // WithinBounds button is no longer pressed if touchpoint leaves bounds + withinBoundsTappedSpy.clear(); + p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::mouseGesturePolicyWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>("WithinBounds"); + QVERIFY(buttonWithinBounds); + QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); + + // WithinBounds button stays pressed while touchpoint stays within bounds, emits tapped on release + QPoint p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + + // WithinBounds button is no longer pressed if touchpoint leaves bounds + withinBoundsTappedSpy.clear(); + p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::mouseMove(window, p1); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::touchGesturePolicyReleaseWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>("ReleaseWithinBounds"); + QVERIFY(buttonReleaseWithinBounds); + QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); + + // ReleaseWithinBounds button stays pressed while touchpoint wanders anywhere, + // then if it comes back within bounds, emits tapped on release + QPoint p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(250, 100); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(25, 15)).toPoint(); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + + // ReleaseWithinBounds button does not emit tapped if released out of bounds + releaseWithinBoundsTappedSpy.clear(); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::mouseGesturePolicyReleaseWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>("ReleaseWithinBounds"); + QVERIFY(buttonReleaseWithinBounds); + QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); + + // ReleaseWithinBounds button stays pressed while touchpoint wanders anywhere, + // then if it comes back within bounds, emits tapped on release + QPoint p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(250, 100); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(25, 15)).toPoint(); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + + // ReleaseWithinBounds button does not emit tapped if released out of bounds + releaseWithinBoundsTappedSpy.clear(); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::touchMultiTap() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + // Tap once + QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 1); + + // Tap again in exactly the same place (not likely with touch in the real world) + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 2); + + // Tap a third time, nearby + p1 += QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 3); + + // Tap a fourth time, drifting farther away + p1 += QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 4); +} + +void tst_TapHandler::mouseMultiTap() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + // Tap once + QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 1); + + // Tap again in exactly the same place (not likely with touch in the real world) + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 2); + + // Tap a third time, nearby + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 3); + + // Tap a fourth time, drifting farther away + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 4); +} + +void tst_TapHandler::touchLongPress() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold"); + QVERIFY(tapHandler); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); + QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); + QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); + + // Reduce the threshold so that we can get a long press quickly + tapHandler->setLongPressThreshold(0.5); + QCOMPARE(longPressThresholdChangedSpy.count(), 1); + + // Press and hold + QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTRY_COMPARE(longPressedSpy.count(), 1); + timeHeldSpy.wait(); // the longer we hold it, the more this will occur + qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times"; + QVERIFY(timeHeldSpy.count() > 0); + QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere + + // Release and verify that tapped was not emitted + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 0); +} + +void tst_TapHandler::mouseLongPress() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold"); + QVERIFY(tapHandler); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); + QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); + QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); + + // Reduce the threshold so that we can get a long press quickly + tapHandler->setLongPressThreshold(0.5); + QCOMPARE(longPressThresholdChangedSpy.count(), 1); + + // Press and hold + QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTRY_COMPARE(longPressedSpy.count(), 1); + timeHeldSpy.wait(); // the longer we hold it, the more this will occur + qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times"; + QVERIFY(timeHeldSpy.count() > 0); + QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere + + // Release and verify that tapped was not emitted + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 500); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 0); +} + +void tst_TapHandler::buttonsMultiTouch() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(buttonDragThreshold); + QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); + + QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>("WithinBounds"); + QVERIFY(buttonWithinBounds); + QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); + + QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>("ReleaseWithinBounds"); + QVERIFY(buttonReleaseWithinBounds); + QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); + + // can press multiple buttons at the same time + QPoint p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + QPoint p2 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).stationary(1).press(2, p2, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + QPoint p3 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).stationary(1).stationary(2).press(3, p3, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + + // can release top button and press again: others stay pressed the whole time + QTest::touchEvent(window, touchDevice).stationary(2).stationary(3).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 0); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QTest::touchEvent(window, touchDevice).stationary(2).stationary(3).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + + // can release middle button and press again: others stay pressed the whole time + QTest::touchEvent(window, touchDevice).stationary(1).stationary(3).release(2, p2, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QTest::touchEvent(window, touchDevice).stationary(1).stationary(3).press(2, p2, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + + // can release bottom button and press again: others stay pressed the whole time + QTest::touchEvent(window, touchDevice).stationary(1).stationary(2).release(3, p3, window); + QQuickTouchUtils::flush(window); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + QVERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + QTest::touchEvent(window, touchDevice).stationary(1).stationary(2).press(3, p3, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); +} + +QTEST_MAIN(tst_TapHandler) + +#include "tst_qquicktaphandler.moc" + diff --git a/tests/auto/quick/qquickapplication/data/tst_platformname.qml b/tests/auto/quick/qquickapplication/data/tst_platformname.qml new file mode 100644 index 0000000000..1bcd66ac8d --- /dev/null +++ b/tests/auto/quick/qquickapplication/data/tst_platformname.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0; + +Item { + id: root; + property string platformName: Qt.platform.pluginName +} diff --git a/tests/auto/quick/qquickapplication/qquickapplication.pro b/tests/auto/quick/qquickapplication/qquickapplication.pro index c47f5472b7..00b5bb3a18 100644 --- a/tests/auto/quick/qquickapplication/qquickapplication.pro +++ b/tests/auto/quick/qquickapplication/qquickapplication.pro @@ -3,7 +3,8 @@ TARGET = tst_qquickapplication macx:CONFIG -= app_bundle SOURCES += tst_qquickapplication.cpp -OTHER_FILES += data/tst_displayname.qml +OTHER_FILES += data/tst_displayname.qml \ + data/tst_platformname.qml include (../../shared/util.pri) diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp index d780b91260..e428a1fc6e 100644 --- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp +++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp @@ -53,6 +53,7 @@ private slots: void styleHints(); void cleanup(); void displayName(); + void platformName(); private: QQmlEngine engine; @@ -264,6 +265,24 @@ void tst_qquickapplication::displayName() QCOMPARE(QGuiApplication::applicationDisplayName(), name[2]); } +void tst_qquickapplication::platformName() +{ + // Set up QML component + QQmlComponent component(&engine, testFileUrl("tst_platformname.qml")); + QQuickItem *item = qobject_cast<QQuickItem *>(component.create()); + QVERIFY(item); + QQuickView view; + item->setParentItem(view.rootObject()); + + // Get native platform name + QString guiApplicationPlatformName = QGuiApplication::platformName(); + QVERIFY(!guiApplicationPlatformName.isEmpty()); + + // Get platform name from QML component and verify it's same + QString qmlPlatformName = qvariant_cast<QString>(item->property("platformName")); + QCOMPARE(qmlPlatformName, guiApplicationPlatformName); +} + QTEST_MAIN(tst_qquickapplication) #include "tst_qquickapplication.moc" diff --git a/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml b/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml index 81187f3c2f..902920babc 100644 --- a/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml +++ b/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml @@ -18,6 +18,8 @@ Flickable { height: 300 color: "yellow" + objectName: "yellowRect" + Flickable { id: inner objectName: "innerFlickable" @@ -30,6 +32,7 @@ Flickable { Rectangle { anchors.fill: parent anchors.margins: 100 + objectName: "blueRect" color: "blue" } MouseArea { diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index f8277c6895..0bb913d104 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -63,7 +63,13 @@ public: , touchReleases(0) , ungrabs(0) , m_active(false) - { } + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + setAcceptTouchEvents(true); +#else + setAcceptedMouseButtons(Qt::LeftButton); // not really, but we want touch events +#endif + } QPointF pos() const { return m_pos; } @@ -2216,6 +2222,7 @@ Q_DECLARE_METATYPE(QQuickFlickable::BoundsBehavior) void tst_qquickflickable::overshoot() { QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior); + QFETCH(int, boundsMovement); QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("overshoot.qml")); @@ -2232,6 +2239,7 @@ void tst_qquickflickable::overshoot() QCOMPARE(flickable->contentHeight(), 400.0); flickable->setBoundsBehavior(boundsBehavior); + flickable->setBoundsMovement(QQuickFlickable::BoundsMovement(boundsMovement)); // drag past the beginning QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); @@ -2240,23 +2248,30 @@ void tst_qquickflickable::overshoot() QTest::mouseMove(window.data(), QPoint(40, 40)); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 50)); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QVERIFY(flickable->property("minContentX").toReal() < 0.0); + QVERIFY(flickable->property("minContentY").toReal() < 0.0); + } else { + QCOMPARE(flickable->property("minContentX").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 0.0); + } if (boundsBehavior & QQuickFlickable::DragOverBounds) { - QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0); - QCOMPARE(flickable->property("minContentY").toReal(), - flickable->property("minVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("minContentX").toReal(), - flickable->property("minHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); } else { - QCOMPARE(flickable->property("minContentY").toReal(), 0.0); - QCOMPARE(flickable->property("minContentX").toReal(), 0.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + } + if (bool(boundsMovement == QQuickFlickable::FollowBoundsBehavior) == bool(boundsBehavior & QQuickFlickable::DragOverBounds)) { + QCOMPARE(flickable->property("minContentX").toReal(), + flickable->property("minHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("minContentY").toReal(), + flickable->property("minVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("maxContentY").toReal(), 0.0); QCOMPARE(flickable->property("maxContentX").toReal(), 0.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); flickable->setContentX(20.0); flickable->setContentY(20.0); @@ -2266,23 +2281,30 @@ void tst_qquickflickable::overshoot() flick(window.data(), QPoint(10, 10), QPoint(50, 50), 100); QTRY_VERIFY(!flickable->property("flicking").toBool()); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QVERIFY(flickable->property("minContentX").toReal() < 0.0); + QVERIFY(flickable->property("minContentY").toReal() < 0.0); + } else { + QCOMPARE(flickable->property("minContentX").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 0.0); + } if (boundsBehavior & QQuickFlickable::OvershootBounds) { - QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0); - QCOMPARE(flickable->property("minContentY").toReal(), - flickable->property("minVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("minContentX").toReal(), - flickable->property("minHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); } else { - QCOMPARE(flickable->property("minContentY").toReal(), 0.0); - QCOMPARE(flickable->property("minContentX").toReal(), 0.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QCOMPARE(flickable->property("minContentX").toReal(), + flickable->property("minHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("minContentY").toReal(), + flickable->property("minVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("maxContentY").toReal(), 20.0); QCOMPARE(flickable->property("maxContentX").toReal(), 20.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 20.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); flickable->setContentX(200.0); flickable->setContentY(200.0); @@ -2295,23 +2317,30 @@ void tst_qquickflickable::overshoot() QTest::mouseMove(window.data(), QPoint(20, 20)); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + } else { + QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); + } if (boundsBehavior & QQuickFlickable::DragOverBounds) { - QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0); - QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, - flickable->property("maxVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, - flickable->property("maxHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); } else { - QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); - QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, + flickable->property("maxHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, + flickable->property("maxVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("minContentY").toReal(), 200.0); QCOMPARE(flickable->property("minContentX").toReal(), 200.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 200.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); flickable->setContentX(180.0); flickable->setContentY(180.0); @@ -2321,37 +2350,59 @@ void tst_qquickflickable::overshoot() flick(window.data(), QPoint(50, 50), QPoint(10, 10), 100); QTRY_VERIFY(!flickable->property("flicking").toBool()); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + QVERIFY(flickable->property("maxContentY").toReal() > 200.0); + } else { + QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); + } if (boundsBehavior & QQuickFlickable::OvershootBounds) { - QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0); - QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, - flickable->property("maxVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, - flickable->property("maxHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); } else { - QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); - QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, + flickable->property("maxHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, + flickable->property("maxVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("minContentY").toReal(), 180.0); QCOMPARE(flickable->property("minContentX").toReal(), 180.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 180.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); } void tst_qquickflickable::overshoot_data() { QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior"); - - QTest::newRow("StopAtBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds); - QTest::newRow("DragOverBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds); - QTest::newRow("OvershootBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds); - QTest::newRow("DragAndOvershootBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds); + QTest::addColumn<int>("boundsMovement"); + + QTest::newRow("StopAtBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("DragOverBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("OvershootBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("DragAndOvershootBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + + QTest::newRow("DragOverBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) + << int(QQuickFlickable::StopAtBounds); + QTest::newRow("OvershootBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) + << int(QQuickFlickable::StopAtBounds); + QTest::newRow("DragAndOvershootBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) + << int(QQuickFlickable::StopAtBounds); } void tst_qquickflickable::overshoot_reentrant() diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index 115fe53430..a2a65aa803 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -91,6 +91,8 @@ private slots: void sourceSizeChanges(); void correctStatus(); void highdpi(); + void highDpiFillModesAndSizes_data(); + void highDpiFillModesAndSizes(); void hugeImages(); private: @@ -971,6 +973,65 @@ void tst_qquickimage::highdpi() delete obj; } +void tst_qquickimage::highDpiFillModesAndSizes_data() +{ + QTest::addColumn<QQuickImage::FillMode>("fillMode"); + QTest::addColumn<qreal>("expectedHeightAfterSettingWidthTo100"); + QTest::addColumn<qreal>("expectedImplicitHeightAfterSettingWidthTo100"); + QTest::addColumn<qreal>("expectedPaintedWidthAfterSettingWidthTo100"); + QTest::addColumn<qreal>("expectedPaintedHeightAfterSettingWidthTo100"); + + QTest::addRow("Stretch") << QQuickImage::Stretch << 150.0 << 150.0 << 100.0 << 150.0; + QTest::addRow("PreserveAspectFit") << QQuickImage::PreserveAspectFit << 100.0 << 100.0 << 100.0 << 100.0; + QTest::addRow("PreserveAspectCrop") << QQuickImage::PreserveAspectCrop << 150.0 << 150.0 << 150.0 << 150.0; + QTest::addRow("Tile") << QQuickImage::Tile << 150.0 << 150.0 << 100.0 << 150.0; + QTest::addRow("TileVertically") << QQuickImage::TileVertically << 150.0 << 150.0 << 100.0 << 150.0; + QTest::addRow("TileHorizontally") << QQuickImage::TileHorizontally << 150.0 << 150.0 << 100.0 << 150.0; + QTest::addRow("Pad") << QQuickImage::Pad << 150.0 << 150.0 << 150.0 << 150.0; +} + +void tst_qquickimage::highDpiFillModesAndSizes() +{ + QFETCH(QQuickImage::FillMode, fillMode); + QFETCH(qreal, expectedHeightAfterSettingWidthTo100); + QFETCH(qreal, expectedImplicitHeightAfterSettingWidthTo100); + QFETCH(qreal, expectedPaintedWidthAfterSettingWidthTo100); + QFETCH(qreal, expectedPaintedHeightAfterSettingWidthTo100); + + QString componentStr = "import QtQuick 2.0\nImage { source: srcImage; }"; + QQmlComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + + engine.rootContext()->setContextProperty("srcImage", testFileUrl("heart-highdpi@2x.png")); + + QScopedPointer<QQuickImage> image(qobject_cast<QQuickImage*>(component.create())); + QVERIFY(image); + QCOMPARE(image->width(), 150.0); + QCOMPARE(image->height(), 150.0); + QCOMPARE(image->paintedWidth(), 150.0); + QCOMPARE(image->paintedHeight(), 150.0); + QCOMPARE(image->implicitWidth(), 150.0); + QCOMPARE(image->implicitHeight(), 150.0); + QCOMPARE(image->paintedWidth(), 150.0); + QCOMPARE(image->paintedHeight(), 150.0); + + // The implicit size should not change when setting any fillMode here. + image->setFillMode(fillMode); + QCOMPARE(image->fillMode(), fillMode); + QCOMPARE(image->implicitWidth(), 150.0); + QCOMPARE(image->implicitHeight(), 150.0); + QCOMPARE(image->paintedWidth(), 150.0); + QCOMPARE(image->paintedHeight(), 150.0); + + image->setWidth(100.0); + QCOMPARE(image->width(), 100.0); + QCOMPARE(image->height(), expectedHeightAfterSettingWidthTo100); + QCOMPARE(image->implicitWidth(), 150.0); + QCOMPARE(image->implicitHeight(), expectedImplicitHeightAfterSettingWidthTo100); + QCOMPARE(image->paintedWidth(), expectedPaintedWidthAfterSettingWidthTo100); + QCOMPARE(image->paintedHeight(), expectedPaintedHeightAfterSettingWidthTo100); +} + void tst_qquickimage::hugeImages() { QQuickView view; diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 10a3a0bfa8..f4434d9d3f 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -49,7 +49,12 @@ public: : QQuickItem(parent), focused(false), pressCount(0), releaseCount(0) , wheelCount(0), acceptIncomingTouchEvents(true) , touchEventReached(false), timestamp(0) - , lastWheelEventPos(0, 0), lastWheelEventGlobalPos(0, 0) {} + , lastWheelEventPos(0, 0), lastWheelEventGlobalPos(0, 0) + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + setAcceptTouchEvents(true); +#endif + } bool focused; int pressCount; diff --git a/tests/auto/quick/qquickrectangle/data/gradient-multiple.qml b/tests/auto/quick/qquickrectangle/data/gradient-multiple.qml new file mode 100644 index 0000000000..d58c857008 --- /dev/null +++ b/tests/auto/quick/qquickrectangle/data/gradient-multiple.qml @@ -0,0 +1,30 @@ +import QtQuick 2.0 + +Item { + property alias firstRectangle: r1 + property alias secondRectangle: r2 + Rectangle { + id: r1 + gradient: someObject.someGradient + anchors.fill: parent + } + Rectangle { + id: r2 + gradient: someObject.someGradient + anchors.fill: parent + } + + function changeGradient() { + firstStop.color = "red" + secondStop.color = "blue" + } + + QtObject { + id: someObject + property Gradient someGradient: Gradient { + GradientStop { id: firstStop; position: 0.0; color: "gray" } + GradientStop { id: secondStop; position: 1.0; color: "white" } + } + } +} + diff --git a/tests/auto/quick/qquickrectangle/data/gradient-separate.qml b/tests/auto/quick/qquickrectangle/data/gradient-separate.qml new file mode 100644 index 0000000000..8ae3f3296b --- /dev/null +++ b/tests/auto/quick/qquickrectangle/data/gradient-separate.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 + +Rectangle { + + function changeGradient() { + firstStop.color = "red" + secondStop.color = "blue" + } + + QtObject { + id: someObject + property Gradient someGradient: Gradient { + GradientStop { id: firstStop; position: 0.0; color: "gray" } + GradientStop { id: secondStop; position: 1.0; color: "white" } + } + } + + gradient: someObject.someGradient +} + diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp index 65c7e387a0..0d79592e37 100644 --- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp +++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp @@ -32,6 +32,7 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> #include <QtQuick/qquickview.h> +#include <private/qquickitem_p.h> #include <private/qquickrectangle_p.h> #include "../../shared/util.h" @@ -46,6 +47,8 @@ private slots: void color(); void gradient(); void gradient_border(); + void gradient_separate(); + void gradient_multiple(); void antialiasing(); private: @@ -111,6 +114,62 @@ void tst_qquickrectangle::gradient_border() QVERIFY(QTest::qWaitForWindowExposed(&view)); } +// A gradient not defined inline with the Rectangle using it should still change +// that Rectangle. +void tst_qquickrectangle::gradient_separate() +{ + QQuickView view; + view.setSource(testFileUrl("gradient-separate.qml")); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(view.rootObject()); + QVERIFY(rect); + + // Start off clean + QQuickItemPrivate *rectPriv = QQuickItemPrivate::get(rect); + bool isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content; + QVERIFY(!isDirty); + + QMetaObject::invokeMethod(rect, "changeGradient"); + + // Changing the gradient should have scheduled an update of the item. + isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content; + QVERIFY(isDirty); +} + +// When a gradient is changed, every Rectangle connected to it must update. +void tst_qquickrectangle::gradient_multiple() +{ + QQuickView view; + view.setSource(testFileUrl("gradient-multiple.qml")); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QQuickRectangle *firstRect = qobject_cast<QQuickRectangle*>(view.rootObject()->property("firstRectangle").value<QObject*>()); + QQuickRectangle *secondRect = qobject_cast<QQuickRectangle*>(view.rootObject()->property("secondRectangle").value<QObject*>()); + QVERIFY(firstRect); + QVERIFY(secondRect); + + // Start off clean + QQuickItemPrivate *firstRectPriv = QQuickItemPrivate::get(firstRect); + QQuickItemPrivate *secondRectPriv = QQuickItemPrivate::get(secondRect); + bool firstIsDirty = firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content; + bool secondIsDirty = secondRectPriv->dirtyAttributes & QQuickItemPrivate::Content; + QVERIFY(!firstIsDirty); + QVERIFY(!secondIsDirty); + + QMetaObject::invokeMethod(view.rootObject(), "changeGradient"); + + // Changing the gradient should have scheduled an update of both items + firstIsDirty = firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content; + secondIsDirty = secondRectPriv->dirtyAttributes & QQuickItemPrivate::Content; + QVERIFY(firstIsDirty); + QVERIFY(secondIsDirty); +} + void tst_qquickrectangle::antialiasing() { QQmlComponent component(&engine); diff --git a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp index 26b687a4a6..0a3796402a 100644 --- a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp +++ b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp @@ -113,6 +113,9 @@ void tst_qquickscreen::fullScreenList() QQuickScreenInfo *info = qobject_cast<QQuickScreenInfo *>(screensArray.property(i).toQObject()); QVERIFY(info != nullptr); QCOMPARE(screenList[i]->name(), info->name()); + QCOMPARE(screenList[i]->manufacturer(), info->manufacturer()); + QCOMPARE(screenList[i]->model(), info->model()); + QCOMPARE(screenList[i]->serialNumber(), info->serialNumber()); QCOMPARE(screenList[i]->size().width(), info->width()); QCOMPARE(screenList[i]->size().height(), info->height()); QCOMPARE(screenList[i]->availableVirtualGeometry().width(), info->desktopAvailableWidth()); diff --git a/tests/auto/quick/qquickshape/data/pathitem1.qml b/tests/auto/quick/qquickshape/data/pathitem1.qml new file mode 100644 index 0000000000..29ca67b0bb --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem1.qml @@ -0,0 +1,5 @@ +import QtQuick 2.9 +import tst_qquickpathitem 1.0 + +Shape { +} diff --git a/tests/auto/quick/qquickshape/data/pathitem2.qml b/tests/auto/quick/qquickshape/data/pathitem2.qml new file mode 100644 index 0000000000..a255a37af6 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem2.qml @@ -0,0 +1,7 @@ +import QtQuick 2.9 +import tst_qquickpathitem 1.0 + +Shape { + ShapePath { } + ShapePath { } +} diff --git a/tests/auto/quick/qquickshape/data/pathitem3.png b/tests/auto/quick/qquickshape/data/pathitem3.png Binary files differnew file mode 100644 index 0000000000..a8b4483c96 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem3.png diff --git a/tests/auto/quick/qquickshape/data/pathitem3.qml b/tests/auto/quick/qquickshape/data/pathitem3.qml new file mode 100644 index 0000000000..8328f2fc33 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem3.qml @@ -0,0 +1,33 @@ +import QtQuick 2.9 +import tst_qquickpathitem 1.0 + +Item { + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "pathItem" + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } +} diff --git a/tests/auto/quick/qquickshape/data/pathitem4.png b/tests/auto/quick/qquickshape/data/pathitem4.png Binary files differnew file mode 100644 index 0000000000..3a988ba249 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem4.png diff --git a/tests/auto/quick/qquickshape/data/pathitem4.qml b/tests/auto/quick/qquickshape/data/pathitem4.qml new file mode 100644 index 0000000000..635113416f --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem4.qml @@ -0,0 +1,56 @@ +import QtQuick 2.9 +import tst_qquickpathitem 1.0 + +Item { + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "pathItem" + anchors.fill: parent + + ShapePath { + strokeColor: "red" + fillColor: "green" + startX: 40; startY: 30 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathLine { x: 150; y: 80 } + PathQuad { x: 160; y: 30; controlX: 200; controlY: 80 } + } + + ShapePath { + strokeWidth: 10 + fillColor: "transparent" + strokeColor: "blue" + startX: 40; startY: 30 + PathCubic { x: 50; y: 80; control1X: 0; control1Y: 80; control2X: 100; control2Y: 100 } + } + + ShapePath { + fillGradient: LinearGradient { + y2: 150 + GradientStop { position: 0; color: "yellow" } + GradientStop { position: 1; color: "green" } + } + + startX: 10; startY: 100 + PathArc { + relativeX: 50; y: 100 + radiusX: 25; radiusY: 25 + } + PathArc { + relativeX: 50; y: 100 + radiusX: 25; radiusY: 35 + } + PathArc { + relativeX: 50; y: 100 + radiusX: 25; radiusY: 60 + } + PathArc { + relativeX: 50; y: 100 + radiusX: 50; radiusY: 120 + } + } + } +} diff --git a/tests/auto/quick/qquickshape/data/pathitem5.png b/tests/auto/quick/qquickshape/data/pathitem5.png Binary files differnew file mode 100644 index 0000000000..cb5cfd25dc --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem5.png diff --git a/tests/auto/quick/qquickshape/data/pathitem5.qml b/tests/auto/quick/qquickshape/data/pathitem5.qml new file mode 100644 index 0000000000..1bd465d5c0 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem5.qml @@ -0,0 +1,37 @@ +import QtQuick 2.9 +import tst_qquickpathitem 1.0 + +Item { + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "pathItem" + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: RadialGradient { + centerX: 100; centerY: 100; centerRadius: 100 + focalX: 100; focalY: 100; focalRadius: 10 + GradientStop { position: 0; color: "#ffffff" } + GradientStop { position: 0.11; color: "#f9ffa0" } + GradientStop { position: 0.13; color: "#f9ff99" } + GradientStop { position: 0.14; color: "#f3ff86" } + GradientStop { position: 0.49; color: "#93b353" } + GradientStop { position: 0.87; color: "#264619" } + GradientStop { position: 0.96; color: "#0c1306" } + GradientStop { position: 1; color: "#000000" } + } + fillColor: "blue" // ignored with the gradient set + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } +} diff --git a/tests/auto/quick/qquickshape/data/pathitem6.png b/tests/auto/quick/qquickshape/data/pathitem6.png Binary files differnew file mode 100644 index 0000000000..d9e53d6c00 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem6.png diff --git a/tests/auto/quick/qquickshape/data/pathitem6.qml b/tests/auto/quick/qquickshape/data/pathitem6.qml new file mode 100644 index 0000000000..fafcc48196 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem6.qml @@ -0,0 +1,35 @@ +import QtQuick 2.9 +import tst_qquickpathitem 1.0 + +Item { + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "pathItem" + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: ConicalGradient { + centerX: 100; centerY: 100; angle: 45 + GradientStop { position: 0; color: "#00000000" } + GradientStop { position: 0.10; color: "#ffe0cc73" } + GradientStop { position: 0.17; color: "#ffc6a006" } + GradientStop { position: 0.46; color: "#ff600659" } + GradientStop { position: 0.72; color: "#ff0680ac" } + GradientStop { position: 0.92; color: "#ffb9d9e6" } + GradientStop { position: 1.00; color: "#00000000" } + } + fillColor: "blue" // ignored with the gradient set + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } +} diff --git a/tests/auto/quick/qquickshape/qquickshape.pro b/tests/auto/quick/qquickshape/qquickshape.pro new file mode 100644 index 0000000000..29c3502b86 --- /dev/null +++ b/tests/auto/quick/qquickshape/qquickshape.pro @@ -0,0 +1,35 @@ +CONFIG += testcase +TARGET = tst_qquickshape +macos:CONFIG -= app_bundle + +SOURCES += tst_qquickshape.cpp + +include (../../shared/util.pri) +include (../shared/util.pri) + +TESTDATA = data/* + +HEADERS += \ + ../../../../src/imports/shapes/qquickshape_p.h \ + ../../../../src/imports/shapes/qquickshape_p_p.h \ + ../../../../src/imports/shapes/qquickshapegenericrenderer_p.h \ + ../../../../src/imports/shapes/qquickshapesoftwarerenderer_p.h + +SOURCES += \ + ../../../../src/imports/shapes/qquickshape.cpp \ + ../../../../src/imports/shapes/qquickshapegenericrenderer.cpp \ + ../../../../src/imports/shapes/qquickshapesoftwarerenderer.cpp + +qtConfig(opengl) { + HEADERS += \ + ../../../../src/imports/shapes/qquicknvprfunctions_p.h \ + ../../../../src/imports/shapes/qquicknvprfunctions_p_p.h \ + ../../../../src/imports/shapes/qquickshapenvprrenderer_p.h + + SOURCES += \ + ../../../../src/imports/shapes/qquicknvprfunctions.cpp \ + ../../../../src/imports/shapes/qquickshapenvprrenderer.cpp +} + +QT += core-private gui-private qml-private quick-private testlib +qtHaveModule(widgets): QT += widgets diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp new file mode 100644 index 0000000000..1b5b345d19 --- /dev/null +++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtQuick/qquickview.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlexpression.h> +#include <QtQml/qqmlincubator.h> +#include "../../../../src/imports/shapes/qquickshape_p.h" + +#include "../../shared/util.h" +#include "../shared/viewtestutil.h" +#include "../shared/visualtestutil.h" + +using namespace QQuickViewTestUtil; +using namespace QQuickVisualTestUtil; + +class tst_QQuickShape : public QQmlDataTest +{ + Q_OBJECT +public: + tst_QQuickShape(); + +private slots: + void initValues(); + void vpInitValues(); + void basicShape(); + void changeSignals(); + void render(); + void renderWithMultipleSp(); + void radialGrad(); + void conicalGrad(); +}; + +tst_QQuickShape::tst_QQuickShape() +{ + // Force the software backend to get reliable rendering results regardless of the hw and drivers. + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); + + const char *uri = "tst_qquickpathitem"; + qmlRegisterType<QQuickShape>(uri, 1, 0, "Shape"); + qmlRegisterType<QQuickShapePath>(uri, 1, 0, "ShapePath"); + qmlRegisterUncreatableType<QQuickShapeGradient>(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); + qmlRegisterType<QQuickShapeLinearGradient>(uri, 1, 0, "LinearGradient"); + qmlRegisterType<QQuickShapeRadialGradient>(uri, 1, 0, "RadialGradient"); + qmlRegisterType<QQuickShapeConicalGradient>(uri, 1, 0, "ConicalGradient"); +} + +void tst_QQuickShape::initValues() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("pathitem1.qml")); + QQuickShape *obj = qobject_cast<QQuickShape *>(c.create()); + + QVERIFY(obj != nullptr); + QVERIFY(obj->rendererType() == QQuickShape::UnknownRenderer); + QVERIFY(!obj->asynchronous()); + QVERIFY(obj->vendorExtensionsEnabled()); + QVERIFY(obj->status() == QQuickShape::Null); + auto vps = obj->data(); + QVERIFY(vps.count(&vps) == 0); + + delete obj; +} + +void tst_QQuickShape::vpInitValues() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("pathitem2.qml")); + QQuickShape *obj = qobject_cast<QQuickShape *>(c.create()); + + QVERIFY(obj != nullptr); + QVERIFY(obj->rendererType() == QQuickShape::UnknownRenderer); + QVERIFY(!obj->asynchronous()); + QVERIFY(obj->vendorExtensionsEnabled()); + QVERIFY(obj->status() == QQuickShape::Null); + auto vps = obj->data(); + QVERIFY(vps.count(&vps) == 2); + + QQuickShapePath *vp = qobject_cast<QQuickShapePath *>(vps.at(&vps, 0)); + QVERIFY(vp != nullptr); + QQmlListReference pathList(vp, "pathElements"); + QCOMPARE(pathList.count(), 0); + QCOMPARE(vp->strokeColor(), QColor(Qt::white)); + QCOMPARE(vp->strokeWidth(), 1.0f); + QCOMPARE(vp->fillColor(), QColor(Qt::white)); + QCOMPARE(vp->fillRule(), QQuickShapePath::OddEvenFill); + QCOMPARE(vp->joinStyle(), QQuickShapePath::BevelJoin); + QCOMPARE(vp->miterLimit(), 2); + QCOMPARE(vp->capStyle(), QQuickShapePath::SquareCap); + QCOMPARE(vp->strokeStyle(), QQuickShapePath::SolidLine); + QCOMPARE(vp->dashOffset(), 0.0f); + QCOMPARE(vp->dashPattern(), QVector<qreal>() << 4 << 2); + QVERIFY(!vp->fillGradient()); + + delete obj; +} + +void tst_QQuickShape::basicShape() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem3.qml")); + qApp->processEvents(); + + QQuickShape *obj = findItem<QQuickShape>(window->rootObject(), "pathItem"); + QVERIFY(obj != nullptr); + QQmlListReference list(obj, "data"); + QCOMPARE(list.count(), 1); + QQuickShapePath *vp = qobject_cast<QQuickShapePath *>(list.at(0)); + QVERIFY(vp != nullptr); + QCOMPARE(vp->strokeWidth(), 4.0f); + QVERIFY(vp->fillGradient() != nullptr); + QCOMPARE(vp->strokeStyle(), QQuickShapePath::DashLine); + + vp->setStrokeWidth(5.0f); + QCOMPARE(vp->strokeWidth(), 5.0f); + + QQuickShapeLinearGradient *lgrad = qobject_cast<QQuickShapeLinearGradient *>(vp->fillGradient()); + QVERIFY(lgrad != nullptr); + QCOMPARE(lgrad->spread(), QQuickShapeGradient::PadSpread); + QCOMPARE(lgrad->x1(), 20.0f); + QQmlListReference stopList(lgrad, "stops"); + QCOMPARE(stopList.count(), 5); + QVERIFY(stopList.at(2) != nullptr); + + QQuickPath *path = vp; + QCOMPARE(path->startX(), 20.0f); + QQmlListReference pathList(path, "pathElements"); + QCOMPARE(pathList.count(), 3); +} + +void tst_QQuickShape::changeSignals() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem3.qml")); + qApp->processEvents(); + + QQuickShape *obj = findItem<QQuickShape>(window->rootObject(), "pathItem"); + QVERIFY(obj != nullptr); + + QSignalSpy asyncPropSpy(obj, SIGNAL(asynchronousChanged())); + obj->setAsynchronous(true); + obj->setAsynchronous(false); + QCOMPARE(asyncPropSpy.count(), 2); + + QQmlListReference list(obj, "data"); + QQuickShapePath *vp = qobject_cast<QQuickShapePath *>(list.at(0)); + QVERIFY(vp != nullptr); + + // Verify that VisualPath property changes emit shapePathChanged(). + QSignalSpy vpChangeSpy(vp, SIGNAL(shapePathChanged())); + QSignalSpy strokeColorPropSpy(vp, SIGNAL(strokeColorChanged())); + vp->setStrokeColor(Qt::blue); + vp->setStrokeWidth(1.0f); + QQuickShapeGradient *g = vp->fillGradient(); + vp->setFillGradient(nullptr); + vp->setFillColor(Qt::yellow); + vp->setFillRule(QQuickShapePath::WindingFill); + vp->setJoinStyle(QQuickShapePath::MiterJoin); + vp->setMiterLimit(5); + vp->setCapStyle(QQuickShapePath::RoundCap); + vp->setDashOffset(10); + vp->setDashPattern(QVector<qreal>() << 1 << 2 << 3 << 4); + QCOMPARE(strokeColorPropSpy.count(), 1); + QCOMPARE(vpChangeSpy.count(), 10); + + // Verify that property changes from Path and its elements bubble up and result in shapePathChanged(). + QQuickPath *path = vp; + path->setStartX(30); + QCOMPARE(vpChangeSpy.count(), 11); + QQmlListReference pathList(path, "pathElements"); + qobject_cast<QQuickPathLine *>(pathList.at(1))->setY(200); + QCOMPARE(vpChangeSpy.count(), 12); + + // Verify that property changes from the gradient bubble up and result in shapePathChanged(). + vp->setFillGradient(g); + QCOMPARE(vpChangeSpy.count(), 13); + QQuickShapeLinearGradient *lgrad = qobject_cast<QQuickShapeLinearGradient *>(g); + lgrad->setX2(200); + QCOMPARE(vpChangeSpy.count(), 14); + QQmlListReference stopList(lgrad, "stops"); + QCOMPARE(stopList.count(), 5); + qobject_cast<QQuickGradientStop *>(stopList.at(1))->setPosition(0.3); + QCOMPARE(vpChangeSpy.count(), 15); + qobject_cast<QQuickGradientStop *>(stopList.at(1))->setColor(Qt::black); + QCOMPARE(vpChangeSpy.count(), 16); +} + +void tst_QQuickShape::render() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem3.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem3.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QVERIFY(QQuickVisualTestUtil::compareImages(img.convertToFormat(refImg.format()), refImg)); +} + +void tst_QQuickShape::renderWithMultipleSp() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem4.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem4.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QVERIFY(QQuickVisualTestUtil::compareImages(img.convertToFormat(refImg.format()), refImg)); +} + +void tst_QQuickShape::radialGrad() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem5.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem5.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QVERIFY(QQuickVisualTestUtil::compareImages(img.convertToFormat(refImg.format()), refImg)); +} + +void tst_QQuickShape::conicalGrad() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem6.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem6.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QVERIFY(QQuickVisualTestUtil::compareImages(img.convertToFormat(refImg.format()), refImg)); +} + +QTEST_MAIN(tst_QQuickShape) + +#include "tst_qquickshape.moc" diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index c5fa0e19fa..4e643bb9d9 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -723,6 +723,61 @@ void tst_qquicktext::textFormat() QCOMPARE(text->textFormat(), QQuickText::AutoText); QCOMPARE(spy.count(), 2); } + + { + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\n Text { text: \"<b>Hello</b>\" }", QUrl()); + QScopedPointer<QObject> object(component.create()); + QQuickText *text = qobject_cast<QQuickText *>(object.data()); + QVERIFY(text); + QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(text); + QVERIFY(textPrivate); + + QCOMPARE(text->textFormat(), QQuickText::AutoText); + QVERIFY(!textPrivate->layout.formats().isEmpty()); + + text->setTextFormat(QQuickText::StyledText); + QVERIFY(!textPrivate->layout.formats().isEmpty()); + + text->setTextFormat(QQuickText::PlainText); + QVERIFY(textPrivate->layout.formats().isEmpty()); + + text->setTextFormat(QQuickText::AutoText); + QVERIFY(!textPrivate->layout.formats().isEmpty()); + } + + { + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nText { text: \"Hello\"; elide: Text.ElideRight }", QUrl::fromLocalFile("")); + QScopedPointer<QObject> object(component.create()); + QQuickText *text = qobject_cast<QQuickText *>(object.data()); + QVERIFY(text); + QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(text); + QVERIFY(textPrivate); + + // underline a mnemonic + QVector<QTextLayout::FormatRange> formats; + QTextLayout::FormatRange range; + range.start = 0; + range.length = 1; + range.format.setFontUnderline(true); + formats << range; + + // the mnemonic format should be retained + textPrivate->layout.setFormats(formats); + text->forceLayout(); + QCOMPARE(textPrivate->layout.formats(), formats); + + // and carried over to the elide layout + text->setWidth(text->implicitWidth() - 1); + QVERIFY(textPrivate->elideLayout); + QCOMPARE(textPrivate->elideLayout->formats(), formats); + + // but cleared when the text changes + text->setText("Changed"); + QVERIFY(textPrivate->elideLayout); + QVERIFY(textPrivate->layout.formats().isEmpty()); + } } //the alignment tests may be trivial o.oa diff --git a/tests/auto/quick/qquickwindow/BLACKLIST b/tests/auto/quick/qquickwindow/BLACKLIST new file mode 100644 index 0000000000..3c000b36f3 --- /dev/null +++ b/tests/auto/quick/qquickwindow/BLACKLIST @@ -0,0 +1,4 @@ +# QTBUG-62177 +[attachedProperty] +windows +osx diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index da1373e11b..adcdca4c3c 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -37,6 +37,7 @@ #include <QtQml/QQmlComponent> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickloader_p.h> +#include <QtQuick/private/qquickmousearea_p.h> #include "../../shared/util.h" #include "../shared/visualtestutil.h" #include "../shared/viewtestutil.h" @@ -47,6 +48,8 @@ #include <QOpenGLFunctions> #include <QSGRendererInterface> +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + struct TouchEventData { QEvent::Type type; QWidget *widget; @@ -140,7 +143,7 @@ class TestTouchItem : public QQuickRectangle public: TestTouchItem(QQuickItem *parent = 0) : QQuickRectangle(parent), acceptTouchEvents(true), acceptMouseEvents(true), - mousePressId(0), + mousePressCount(0), mouseMoveCount(0), spinLoopWhenPressed(false), touchEventCount(0) { border()->setWidth(1); @@ -159,9 +162,10 @@ public: lastMousePos = QPointF(); lastMouseCapabilityFlags = 0; touchEventCount = 0; + mouseMoveCount = 0; } - static void clearMousePressCounter() + static void clearMouseEventCounters() { mousePressNum = mouseMoveNum = mouseReleaseNum = 0; } @@ -174,7 +178,8 @@ public: bool acceptTouchEvents; bool acceptMouseEvents; TouchEventData lastEvent; - int mousePressId; + int mousePressCount; + int mouseMoveCount; bool spinLoopWhenPressed; int touchEventCount; QVector2D lastVelocity; @@ -204,7 +209,7 @@ public: e->ignore(); return; } - mousePressId = ++mousePressNum; + mousePressCount = ++mousePressNum; lastMousePos = e->pos(); lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); } @@ -214,7 +219,7 @@ public: e->ignore(); return; } - ++mouseMoveNum; + mouseMoveCount = ++mouseMoveNum; lastVelocityFromMouseMove = QGuiApplicationPrivate::mouseEventVelocity(e); lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); lastMousePos = e->pos(); @@ -230,10 +235,19 @@ public: 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; + bool childMouseEventFilter(QQuickItem *item, QEvent *e) { + qCDebug(lcTests) << objectName() << "filtering" << e << "ahead of delivery to" << item->metaObject()->className() << item->objectName(); + switch (e->type()) { + case QEvent::MouseButtonPress: + mousePressCount = ++mousePressNum; + break; + case QEvent::MouseMove: + mouseMoveCount = ++mouseMoveNum; + break; + default: + break; + } + return false; } @@ -368,6 +382,7 @@ private slots: void testHoverChildMouseEventFilter(); void testHoverTimestamp(); + void test_circleMapItem(); void pointerEventTypeAndPointCount(); @@ -507,7 +522,7 @@ void tst_qquickwindow::constantUpdatesOnWindow() void tst_qquickwindow::touchEvent_basic() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -636,7 +651,7 @@ void tst_qquickwindow::touchEvent_basic() void tst_qquickwindow::touchEvent_propagation() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QFETCH(bool, acceptTouchEvents); QFETCH(bool, acceptMouseEvents); @@ -783,7 +798,7 @@ void tst_qquickwindow::touchEvent_propagation_data() void tst_qquickwindow::touchEvent_cancel() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -817,7 +832,7 @@ void tst_qquickwindow::touchEvent_cancel() void tst_qquickwindow::touchEvent_reentrant() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -856,7 +871,7 @@ void tst_qquickwindow::touchEvent_reentrant() void tst_qquickwindow::touchEvent_velocity() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -991,7 +1006,7 @@ void tst_qquickwindow::mouseFromTouch_basic() // should result in sending mouse events generated from the touch // with the new event propagation system. - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); window->resize(250, 250); @@ -1092,7 +1107,7 @@ void tst_qquickwindow::clearWindow() void tst_qquickwindow::mouseFiltering() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -1106,6 +1121,11 @@ void tst_qquickwindow::mouseFiltering() bottomItem->setObjectName("Bottom Item"); bottomItem->setSize(QSizeF(150, 150)); + TestTouchItem *siblingItem = new TestTouchItem(bottomItem); + siblingItem->setObjectName("Sibling of Middle Item"); + siblingItem->setPosition(QPointF(90, 25)); + siblingItem->setSize(QSizeF(150, 150)); + TestTouchItem *middleItem = new TestTouchItem(bottomItem); middleItem->setObjectName("Middle Item"); middleItem->setPosition(QPointF(50, 50)); @@ -1125,9 +1145,41 @@ void tst_qquickwindow::mouseFiltering() // 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); + QTRY_COMPARE(middleItem->mousePressCount, 1); + QTRY_COMPARE(bottomItem->mousePressCount, 2); + QTRY_COMPARE(topItem->mousePressCount, 3); + QCOMPARE(siblingItem->mousePressCount, 0); + + QTest::mouseRelease(window, Qt::LeftButton, 0, pos); + topItem->clearMouseEventCounters(); + middleItem->clearMouseEventCounters(); + bottomItem->clearMouseEventCounters(); + siblingItem->clearMouseEventCounters(); + + // Repeat, but this time have the top item accept the press + topItem->acceptMouseEvents = true; + + 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->mousePressCount, 1); + QTRY_COMPARE(bottomItem->mousePressCount, 2); + QTRY_COMPARE(topItem->mousePressCount, 3); + QCOMPARE(siblingItem->mousePressCount, 0); + + pos += QPoint(50, 50); + QTest::mouseMove(window, pos); + + // The top item has grabbed, so the move goes there, but again + // all the ancestors can filter, even when the mouse is outside their bounds + QTRY_COMPARE(middleItem->mouseMoveCount, 1); + QTRY_COMPARE(bottomItem->mouseMoveCount, 2); + QTRY_COMPARE(topItem->mouseMoveCount, 3); + QCOMPARE(siblingItem->mouseMoveCount, 0); // clean up mouse press state for the next tests QTest::mouseRelease(window, Qt::LeftButton, 0, pos); @@ -1374,12 +1426,6 @@ void tst_qquickwindow::headless() if (isGL) QVERIFY(!window->isSceneGraphInitialized()); } -#if QT_CONFIG(opengl) - if (QGuiApplication::platformName() == QLatin1String("windows") - && QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - QSKIP("Crashes on Windows/ANGLE, QTBUG-42967"); - } -#endif // Destroy the native windowing system buffers window->destroy(); QVERIFY(!window->handle()); @@ -2531,6 +2577,84 @@ void tst_qquickwindow::testHoverTimestamp() QCOMPARE(hoverConsumer->hoverTimestamps.last(), 5UL); } +class CircleItem : public QQuickRectangle +{ +public: + CircleItem(QQuickItem *parent = 0) : QQuickRectangle(parent) { } + + void setRadius(qreal radius) { + const qreal diameter = radius*2; + setWidth(diameter); + setHeight(diameter); + } + + bool childMouseEventFilter(QQuickItem *item, QEvent *event) override + { + Q_UNUSED(item) + if (event->type() == QEvent::MouseButtonPress && !contains(static_cast<QMouseEvent*>(event)->pos())) { + // This is an evil hack: in case of items that are not rectangles, we never accept the event. + // Instead the events are now delivered to QDeclarativeGeoMapItemBase which doesn't to anything with them. + // The map below it still works since it filters events and steals the events at some point. + event->setAccepted(false); + return true; + } + return false; + } + + virtual bool contains(const QPointF &pos) const override { + // returns true if the point is inside the the embedded circle inside the (square) rect + const float radius = (float)width()/2; + const QVector2D center(radius, radius); + const QVector2D dx = QVector2D(pos) - center; + const bool ret = dx.lengthSquared() < radius*radius; + return ret; + } +}; + +void tst_qquickwindow::test_circleMapItem() +{ + QQuickWindow window; + + window.resize(250, 250); + window.setPosition(100, 100); + window.setTitle(QTest::currentTestFunction()); + + QQuickItem *root = window.contentItem(); + QQuickMouseArea *mab = new QQuickMouseArea(root); + mab->setObjectName("Bottom MouseArea"); + mab->setSize(QSizeF(100, 100)); + + CircleItem *topItem = new CircleItem(root); + topItem->setFiltersChildMouseEvents(true); + topItem->setColor(Qt::green); + topItem->setObjectName("Top Item"); + topItem->setPosition(QPointF(30, 30)); + topItem->setRadius(20); + QQuickMouseArea *mat = new QQuickMouseArea(topItem); + mat->setObjectName("Top Item/MouseArea"); + mat->setSize(QSizeF(40, 40)); + + QSignalSpy bottomSpy(mab, SIGNAL(clicked(QQuickMouseEvent *))); + QSignalSpy topSpy(mat, SIGNAL(clicked(QQuickMouseEvent *))); + + window.show(); + QTest::qWaitForWindowExposed(&window); + QTest::qWait(1000); + + QPoint pos(50, 50); + QTest::mouseClick(&window, Qt::LeftButton, Qt::KeyboardModifiers(), pos); + + QCOMPARE(topSpy.count(), 1); + QCOMPARE(bottomSpy.count(), 0); + + // Outside the "Circles" "input area", but on top of the bottomItem rectangle + pos = QPoint(66, 66); + QTest::mouseClick(&window, Qt::LeftButton, Qt::KeyboardModifiers(), pos); + + QCOMPARE(bottomSpy.count(), 1); + QCOMPARE(topSpy.count(), 1); +} + void tst_qquickwindow::pointerEventTypeAndPointCount() { QPointF localPosition(33, 66); diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 00be8240e5..9b7740646a 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -47,6 +47,7 @@ PRIVATETESTS += \ !qtHaveModule(xmlpatterns): PRIVATETESTS -= qquickxmllistmodel QUICKTESTS = \ + pointerhandlers \ qquickaccessible \ qquickanchors \ qquickanimatedimage \ @@ -69,6 +70,7 @@ QUICKTESTS = \ qquickmousearea \ qquickmultipointtoucharea \ qquickpainteditem \ + qquickshape \ qquickpathview \ qquickpincharea \ qquickpositioners \ diff --git a/tests/auto/quick/touchmouse/BLACKLIST b/tests/auto/quick/touchmouse/BLACKLIST new file mode 100644 index 0000000000..ac0352d10b --- /dev/null +++ b/tests/auto/quick/touchmouse/BLACKLIST @@ -0,0 +1,3 @@ +# QTBUG-40856 hover regression on pointerhandler branch: TODO fix before merging to dev +[hoverEnabled] +* diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 39f2961927..646317078b 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -30,6 +30,7 @@ #include <QtTest/QtTest> #include <QtGui/qstylehints.h> +#include <private/qdebug_p.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> @@ -62,6 +63,21 @@ struct Event QList<QTouchEvent::TouchPoint> points; }; +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const struct Event &event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "Event("; + QtDebugUtils::formatQEnum(dbg, event.type); + if (event.points.isEmpty()) + dbg << " @ " << event.mousePos << " global " << event.mousePosGlobal; + else + dbg << ", " << event.points.count() << " touchpoints: " << event.points; + dbg << ')'; + return dbg; +} +#endif + class EventItem : public QQuickItem { Q_OBJECT @@ -74,6 +90,9 @@ public: : QQuickItem(parent), touchUngrabCount(0), acceptMouse(false), acceptTouch(false), filterTouch(false), point0(-1) { setAcceptedMouseButtons(Qt::LeftButton); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + setAcceptTouchEvents(true); +#endif } void touchEvent(QTouchEvent *event) @@ -572,7 +591,7 @@ void tst_TouchMouse::buttonOnFlickable() QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data()); QVERIFY(windowPriv->touchMouseId != -1); auto pointerEvent = windowPriv->pointerEventInstance(QQuickPointerDevice::touchDevices().at(0)); - QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), eventItem1); QCOMPARE(window->mouseGrabberItem(), eventItem1); int dragDelta = -qApp->styleHints()->startDragDistance(); @@ -594,7 +613,7 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(window->mouseGrabberItem(), flickable); QVERIFY(windowPriv->touchMouseId != -1); - QCOMPARE(pointerEvent->point(0)->grabber(), flickable); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), flickable); QVERIFY(flickable->isMovingVertically()); QTest::touchEvent(window.data(), device).release(0, p3, window.data()); @@ -633,7 +652,7 @@ void tst_TouchMouse::touchButtonOnFlickable() QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data()); QVERIFY(windowPriv->touchMouseId == -1); auto pointerEvent = windowPriv->pointerEventInstance(QQuickPointerDevice::touchDevices().at(0)); - QCOMPARE(pointerEvent->point(0)->grabber(), eventItem2); + QCOMPARE(pointerEvent->point(0)->grabberItem(), eventItem2); QCOMPARE(window->mouseGrabberItem(), nullptr); int dragDelta = qApp->styleHints()->startDragDistance() * -0.7; @@ -654,7 +673,7 @@ void tst_TouchMouse::touchButtonOnFlickable() QCOMPARE(eventItem2->touchUngrabCount, 1); QCOMPARE(window->mouseGrabberItem(), flickable); QVERIFY(windowPriv->touchMouseId != -1); - QCOMPARE(pointerEvent->point(0)->grabber(), flickable); + QCOMPARE(pointerEvent->point(0)->grabberItem(), flickable); QVERIFY(flickable->isMovingVertically()); QTest::touchEvent(window.data(), device).release(0, p3, window.data()); @@ -759,7 +778,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QCOMPARE(window->mouseGrabberItem(), flickable); QVERIFY(windowPriv->touchMouseId != -1); auto pointerEvent = windowPriv->pointerEventInstance(QQuickPointerDevice::touchDevices().at(0)); - QCOMPARE(pointerEvent->point(0)->grabber(), flickable); + QCOMPARE(pointerEvent->point(0)->grabberItem(), flickable); QTest::touchEvent(window.data(), device).release(0, p3, window.data()); QQuickTouchUtils::flush(window.data()); diff --git a/tests/benchmarks/qml/creation/tst_creation.cpp b/tests/benchmarks/qml/creation/tst_creation.cpp index 30d7ef902e..91f907ab5e 100644 --- a/tests/benchmarks/qml/creation/tst_creation.cpp +++ b/tests/benchmarks/qml/creation/tst_creation.cpp @@ -385,7 +385,7 @@ void tst_creation::bindings_qml() return; } - QQuickItem *obj = dynamic_cast<QQuickItem *>(component.create()); + QQuickItem *obj = qobject_cast<QQuickItem *>(component.create()); QVERIFY(obj != nullptr); int height = 0; @@ -407,7 +407,7 @@ void tst_creation::bindings_parent_qml() return; } - QQuickItem *obj = dynamic_cast<QQuickItem *>(component.create()); + QQuickItem *obj = qobject_cast<QQuickItem *>(component.create()); QVERIFY(obj != nullptr); int height = 0; diff --git a/tests/manual/pointer/content/FakeFlickable.qml b/tests/manual/pointer/content/FakeFlickable.qml new file mode 100644 index 0000000000..55dafef82e --- /dev/null +++ b/tests/manual/pointer/content/FakeFlickable.qml @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + id: root + default property alias data: __contentItem.data + property alias velocity: anim.velocity + property alias contentX: __contentItem.x // sign is reversed compared to Flickable.contentX + property alias contentY: __contentItem.y // sign is reversed compared to Flickable.contentY + property alias contentWidth: __contentItem.width + property alias contentHeight: __contentItem.height + signal flickStarted + signal flickEnded + + Item { + id: __contentItem + objectName: "__contentItem" + width: childrenRect.width + height: childrenRect.height + + property real xlimit: root.width - __contentItem.width + property real ylimit: root.height - __contentItem.height + + function returnToBounds() { + var startAnim = false + if (x > 0) { + returnToBoundsAnim.x = 0 + startAnim = true + } else if (x < xlimit) { + returnToBoundsAnim.x = xlimit + startAnim = true + } + if (y > 0) { + returnToBoundsAnim.y = 0 + startAnim = true + } else if (y < ylimit) { + returnToBoundsAnim.y = ylimit + startAnim = true + } + if (startAnim) + returnToBoundsAnim.start() + } + + DragHandler { + id: dragHandler + onActiveChanged: if (!active) anim.restart(point.velocity) + } + MomentumAnimation { + id: anim + target: __contentItem + onStarted: root.flickStarted() + onStopped: { + __contentItem.returnToBounds() + root.flickEnded() + } + } + ParallelAnimation { + id: returnToBoundsAnim + property Item target: __contentItem + property int duration: 200 + property real x: 0 + property real y: 0 + + NumberAnimation { + id: xAnim + target: returnToBoundsAnim.target + property: "x" + to: returnToBoundsAnim.x + duration: returnToBoundsAnim.duration + easing.type: Easing.OutQuad + } + NumberAnimation { + id: yAnim + target: returnToBoundsAnim.target + property: "y" + to: returnToBoundsAnim.y + duration: returnToBoundsAnim.duration + easing.type: Easing.OutQuad + } + } + } +} diff --git a/tests/manual/pointer/content/FlashAnimation.qml b/tests/manual/pointer/content/FlashAnimation.qml new file mode 100644 index 0000000000..b628255a3d --- /dev/null +++ b/tests/manual/pointer/content/FlashAnimation.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +SequentialAnimation { + id: tapFlash + running: false + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } +} diff --git a/tests/manual/pointer/content/MomentumAnimation.qml b/tests/manual/pointer/content/MomentumAnimation.qml new file mode 100644 index 0000000000..abd9ac9269 --- /dev/null +++ b/tests/manual/pointer/content/MomentumAnimation.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 + +ParallelAnimation { + id: root + property Item target: null + property int duration: 500 + property vector2d velocity: Qt.vector2d(0,0) + + function restart(vel) { + stop() + velocity = vel + start() + } + + NumberAnimation { + id: xAnim + target: root.target + property: "x" + to: target.x + velocity.x / duration * 100000 + duration: root.duration + easing.type: Easing.OutQuad + } + NumberAnimation { + id: yAnim + target: root.target + property: "y" + to: target.y + velocity.y / duration * 100000 + duration: root.duration + easing.type: Easing.OutQuad + } +} diff --git a/tests/manual/pointer/content/MouseAreaButton.qml b/tests/manual/pointer/content/MouseAreaButton.qml new file mode 100644 index 0000000000..de1d972386 --- /dev/null +++ b/tests/manual/pointer/content/MouseAreaButton.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Window 2.1 + +Item { + id: container + + property alias text: buttonLabel.text + property alias label: buttonLabel + signal clicked + property alias containsMouse: mouseArea.containsMouse + property alias pressed: mouseArea.pressed + implicitHeight: Math.max(Screen.pixelDensity * 7, buttonLabel.implicitHeight * 1.2) + implicitWidth: Math.max(Screen.pixelDensity * 11, buttonLabel.implicitWidth * 1.3) + height: implicitHeight + width: implicitWidth + + SystemPalette { id: palette } + + Rectangle { + id: frame + anchors.fill: parent + color: palette.button + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? Qt.darker(palette.button, 1.3) : palette.button } + GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) } + } + antialiasing: true + radius: height / 6 + border.color: Qt.darker(palette.button, 1.5) + border.width: 1 + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: container.clicked() + hoverEnabled: true + } + + Text { + id: buttonLabel + text: container.text + color: palette.buttonText + anchors.centerIn: parent + } +} diff --git a/tests/manual/pointer/content/MouseAreaSlider.qml b/tests/manual/pointer/content/MouseAreaSlider.qml new file mode 100644 index 0000000000..c34c528c7d --- /dev/null +++ b/tests/manual/pointer/content/MouseAreaSlider.qml @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 + +Item { + id: root + property int value: 50 + property int maximumValue: 99 + property alias label: label.text + + Rectangle { + id: slot + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 10 + anchors.topMargin: 30 + anchors.bottomMargin: 30 + anchors.horizontalCenter: parent.horizontalCenter + width: 10 + color: "black" + radius: width / 2 + smooth: true + } + + Rectangle { + // RectangularGlow is better, but that's a different module + id: glow + anchors.fill: knob + anchors.margins: -5 + anchors.leftMargin: -2 + anchors.horizontalCenterOffset: 1 + radius: 5 + color: "#4400FFFF" + opacity: ma.pressed ? 1 : 0 + } + Image { + id: knob + source: "../resources/mixer-knob.png" + antialiasing: true + x: slot.x - width / 2 + slot.width / 2 + height: root.width / 2 + width: implicitWidth / implicitHeight * height + property bool programmatic: false + property real multiplier: root.maximumValue / (ma.drag.maximumY - ma.drag.minimumY) + onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - ma.drag.minimumY) * multiplier + transformOrigin: Item.Center + function setValue(value) { knob.y = ma.drag.maximumY - value / knob.multiplier } + MouseArea { + id: ma + anchors.fill: parent + drag.target: parent + drag.minimumX: knob.x + drag.maximumX: knob.x + drag.minimumY: slot.y + drag.maximumY: slot.height + slot.y - knob.height + } + } + + Text { + font.pointSize: 16 + color: "red" + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: root.value + } + + Text { + id: label + font.pointSize: 12 + color: "red" + anchors.top: parent.top + anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + } + + onHeightChanged: { + knob.programmatic = true + knob.setValue(root.value) + knob.programmatic = false + } +} diff --git a/tests/manual/pointer/content/MptaButton.qml b/tests/manual/pointer/content/MptaButton.qml new file mode 100644 index 0000000000..836744822b --- /dev/null +++ b/tests/manual/pointer/content/MptaButton.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Window 2.1 + +Item { + id: container + + property alias text: buttonLabel.text + property alias label: buttonLabel + signal clicked + property alias pressed: point.pressed + implicitHeight: Math.max(Screen.pixelDensity * 7, buttonLabel.implicitHeight * 1.2) + implicitWidth: Math.max(Screen.pixelDensity * 11, buttonLabel.implicitWidth * 1.3) + height: implicitHeight + width: implicitWidth + + SystemPalette { id: palette } + + Rectangle { + id: frame + anchors.fill: parent + color: palette.button + gradient: Gradient { + GradientStop { position: 0.0; color: container.pressed ? Qt.darker(palette.button, 1.3) : palette.button } + GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) } + } + antialiasing: true + radius: height / 6 + border.color: Qt.darker(palette.button, 1.5) + border.width: 1 + } + + MultiPointTouchArea { + id: mpta + anchors.fill: parent + touchPoints: [ TouchPoint { + id: point + onPressedChanged: if (!pressed) container.clicked() + } ] + } + + Text { + id: buttonLabel + text: container.text + color: palette.buttonText + anchors.centerIn: parent + } +} diff --git a/tests/manual/pointer/content/MultiButton.qml b/tests/manual/pointer/content/MultiButton.qml new file mode 100644 index 0000000000..4a737dc9b0 --- /dev/null +++ b/tests/manual/pointer/content/MultiButton.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + property alias label: label.text + property alias pressed: tap.isPressed + property bool checked: false + property alias gesturePolicy: tap.gesturePolicy + signal tapped + + width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0 + border.color: "#9f9d9a"; border.width: 1; radius: height / 4; antialiasing: true + + gradient: Gradient { + GradientStop { position: 0.0; color: tap.isPressed ? "#b8b5b2" : "#efebe7" } + GradientStop { position: 1.0; color: "#b8b5b2" } + } + + TapHandler { + id: tap + objectName: label.text + onTapped: { + tapFlash.start() + root.tapped() + } + } + + Text { + id: label + font.pointSize: 14 + text: "Button" + anchors.centerIn: parent + } + + Rectangle { + anchors.fill: parent + color: "transparent" + border.width: 2; radius: root.radius; antialiasing: true + opacity: tapFlash.running ? 1 : 0 + FlashAnimation on visible { + id: tapFlash + } + } +} diff --git a/tests/manual/pointer/content/ScrollBar.qml b/tests/manual/pointer/content/ScrollBar.qml new file mode 100644 index 0000000000..ef18ceb98f --- /dev/null +++ b/tests/manual/pointer/content/ScrollBar.qml @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + property real contentHeight: 100 + property FakeFlickable flick: null + property real position + + onPositionChanged: if (flick && (drag.active || tap.active)) { + if (knob.state === "horizontal") + flick.contentX = position * knob.scrollDistance + else if (knob.state === "vertical") + flick.contentY = position * knob.scrollDistance + } + + SystemPalette { id: palette } + + color: palette.button + border.color: Qt.darker(palette.button, 1.5) + gradient: Gradient { + GradientStop { position: 0; color: Qt.darker(palette.button, 1.3) } + GradientStop { position: 1; color: palette.button } + } + antialiasing: true + radius: Math.min(width, height) / 6 + width: 20 + height: 20 + + TapHandler { + id: tap + onTapped: { + if (knob.state === "horizontal") + knob.x = position.x - knob.width / 2 + else if (knob.state === "vertical") + knob.y = position.y - knob.height / 2 + } + } + + Rectangle { + id: knob + border.color: "black" + border.width: 1 + gradient: Gradient { + GradientStop { position: 0; color: palette.button } + GradientStop { position: 1; color: Qt.darker(palette.button, 1.3) } + } + radius: 2 + antialiasing: true + state: root.height > root.width ? "vertical" : root.height < root.width ? "horizontal" : "" + property real scrollDistance: 0 + property real scrolledX: 0 + property real scrolledY: 0 + property real max: 0 + + Binding on x { + value: knob.scrolledX + when: !drag.active + } + + Binding on y { + value: knob.scrolledY + when: !drag.active + } + + states: [ + // We will control the horizontal. We will control the vertical. + State { + name: "horizontal" + PropertyChanges { + target: knob + max: root.width - knob.width + scrolledX: Math.min(max, Math.max(0, max * flick.contentX / (flick.width - flick.contentWidth))) + scrolledY: 1 + scrollDistance: flick.width - flick.contentWidth + width: flick.width * (flick.width / flick.contentWidth) - (height - anchors.margins) * 2 + height: root.height - 2 + } + PropertyChanges { + target: drag + xAxis.minimum: 0 + xAxis.maximum: knob.max + yAxis.minimum: 1 + yAxis.maximum: 1 + } + PropertyChanges { + target: root + position: knob.x / drag.xAxis.maximum + } + }, + State { + name: "vertical" + PropertyChanges { + target: knob + max: root.height - knob.height + scrolledX: 1 + scrolledY: Math.min(max, Math.max(0, max * flick.contentY / (flick.height - flick.contentHeight))) + scrollDistance: flick.height - flick.contentHeight + width: root.width - 2 + height: root.width - 2 + } + PropertyChanges { + target: drag + xAxis.minimum: 1 + xAxis.maximum: 1 + yAxis.minimum: 0 + yAxis.maximum: knob.max + } + PropertyChanges { + target: root + position: knob.y / drag.yAxis.maximum + } + } + ] + + DragHandler { + id: drag + } + } +} diff --git a/tests/manual/pointer/content/Slider.qml b/tests/manual/pointer/content/Slider.qml new file mode 100644 index 0000000000..809e4c5f1c --- /dev/null +++ b/tests/manual/pointer/content/Slider.qml @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + id: root + property int value: 50 + property int maximumValue: 99 + property alias label: label.text + property alias tapEnabled: tap.enabled + property alias pressed: tap.isPressed + signal tapped + + DragHandler { + id: dragHandler + objectName: label.text + " DragHandler" + target: knob + xAxis.enabled: false + yAxis.minimum: slot.y + yAxis.maximum: slot.height + slot.y - knob.height + } + + Rectangle { + id: slot + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 10 + anchors.topMargin: 30 + anchors.bottomMargin: 30 + anchors.horizontalCenter: parent.horizontalCenter + width: 10 + color: "black" + radius: width / 2 + smooth: true + } + + Rectangle { + // RectangularGlow is better, but that's a different module + id: glow + anchors.fill: knob + anchors.margins: -5 + anchors.leftMargin: -2 + anchors.horizontalCenterOffset: 1 + radius: 5 + color: "#4400FFFF" + opacity: tap.isPressed || tapFlash.running ? 1 : 0 + FlashAnimation on visible { + id: tapFlash + } + } + Image { + id: knob + source: "../resources/mixer-knob.png" + antialiasing: true + x: slot.x - width / 2 + slot.width / 2 + height: root.width / 2 + width: implicitWidth / implicitHeight * height + property bool programmatic: false + property real multiplier: root.maximumValue / (dragHandler.yAxis.maximum - dragHandler.yAxis.minimum) + onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - dragHandler.yAxis.minimum) * multiplier + transformOrigin: Item.Center + function setValue(value) { knob.y = dragHandler.yAxis.maximum - value / knob.multiplier } + TapHandler { + id: tap + objectName: label.text + " TapHandler" + gesturePolicy: TapHandler.DragThreshold + onTapped: { + tapFlash.start() + root.tapped + } + } + } + + Text { + font.pointSize: 16 + color: "red" + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: root.value + } + + Text { + id: label + font.pointSize: 12 + color: "red" + anchors.top: parent.top + anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + } + + onHeightChanged: { + knob.programmatic = true + knob.setValue(root.value) + knob.programmatic = false + } +} diff --git a/tests/manual/pointer/content/TapHandlerButton.qml b/tests/manual/pointer/content/TapHandlerButton.qml new file mode 100644 index 0000000000..e40c539686 --- /dev/null +++ b/tests/manual/pointer/content/TapHandlerButton.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Window 2.1 +import Qt.labs.handlers 1.0 + +Item { + id: container + + property alias text: buttonLabel.text + property alias label: buttonLabel + signal clicked + property alias pressed: th.isPressed + implicitHeight: Math.max(Screen.pixelDensity * 7, buttonLabel.implicitHeight * 1.2) + implicitWidth: Math.max(Screen.pixelDensity * 11, buttonLabel.implicitWidth * 1.3) + height: implicitHeight + width: implicitWidth + + SystemPalette { id: palette } + + Rectangle { + id: frame + anchors.fill: parent + color: palette.button + gradient: Gradient { + GradientStop { position: 0.0; color: container.pressed ? Qt.darker(palette.button, 1.3) : palette.button } + GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) } + } + antialiasing: true + radius: height / 6 + border.color: Qt.darker(palette.button, 1.5) + border.width: 1 + } + + TapHandler { + id: th + onTapped: container.clicked() + } + + Text { + id: buttonLabel + text: container.text + color: palette.buttonText + anchors.centerIn: parent + } +} diff --git a/tests/manual/pointer/fakeFlickable.qml b/tests/manual/pointer/fakeFlickable.qml new file mode 100644 index 0000000000..0c6dbe4558 --- /dev/null +++ b/tests/manual/pointer/fakeFlickable.qml @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 +import "content" + +Rectangle { + color: "#444" + width: 480 + height: 480 + + FakeFlickable { + id: ff + anchors.fill: parent + anchors.rightMargin: rightSB.width + Row { + Item { + width: 100 + height: 400 + Slider { + id: slider + label: "font size" + anchors.fill: parent + maximumValue: 36 + value: 14 + } + } + Text { + id: text + color: "beige" + font.family: "mono" + font.pointSize: slider.value + onTextChanged: console.log("text geom " + width + "x" + height + + ", parent " + parent + " geom " + parent.width + "x" + parent.height) + } + } + + + onFlickStarted: console.log("flick started with velocity " + velocity) + onFlickEnded: console.log("flick ended with velocity " + velocity) + + Component.onCompleted: { + var request = new XMLHttpRequest() + request.open('GET', 'content/FakeFlickable.qml') + request.onreadystatechange = function(event) { + if (request.readyState === XMLHttpRequest.DONE) + text.text = request.responseText + } + request.send() + } + } + + ScrollBar { + id: rightSB + objectName: "rightSB" + flick: ff + height: parent.height - width + anchors.right: parent.right + } + + ScrollBar { + id: bottomSB + objectName: "bottomSB" + flick: ff + width: parent.width - height + anchors.bottom: parent.bottom + } + + Rectangle { + id: cornerCover + color: "lightgray" + width: rightSB.width + height: bottomSB.height + anchors { + right: parent.right + bottom: parent.bottom + } + } +} diff --git a/tests/manual/pointer/flickableWithHandlers.qml b/tests/manual/pointer/flickableWithHandlers.qml new file mode 100644 index 0000000000..8225012591 --- /dev/null +++ b/tests/manual/pointer/flickableWithHandlers.qml @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import Qt.labs.handlers 1.0 +import "qrc:/quick/shared/" as Examples +import "content" + +Rectangle { + id: root + width: 400 + height: 400 + objectName: "root" + color: "#222222" + + Flickable { + anchors.fill: parent + anchors.margins: 10 + anchors.topMargin: 40 + contentHeight: 600 + contentWidth: 600 + pressDelay: pressDelayCB.checked ? 1000 : 0 + + Column { + spacing: 6 + Rectangle { + radius: 5 + width: parent.width - 12 + height: pressDelayCB.implicitHeight + 12 + x: 6 + color: "lightgray" + Examples.CheckBox { + x: 6; y: 6 + id: pressDelayCB + text: "press delay" + } + } + + + Row { + spacing: 6 + Slider { + label: "DragHandler" + value: 49; width: 100; height: 400 + } + MouseAreaSlider { + label: "MouseArea" + value: 49; width: 100; height: 400 + } + Column { + spacing: 6 + MouseAreaButton { + text: "MouseArea" + } + MptaButton { + text: "MultiPointTouchArea" + } + MptaButton { + text: "MultiPointTouchArea" + } + TapHandlerButton { + text: "TapHandler" + } + TapHandlerButton { + text: "TapHandler" + } + } + } + } + } +} diff --git a/tests/manual/pointer/flingAnimation.qml b/tests/manual/pointer/flingAnimation.qml new file mode 100644 index 0000000000..d868fcc498 --- /dev/null +++ b/tests/manual/pointer/flingAnimation.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 +import "content" + +Rectangle { + id: root + width: 640 + height: 480 + color: "black" + + Repeater { + model: 2 + + Image { + id: ball + objectName: "ball" + index + source: "resources/redball.png" + width: 80; height: 80; x: 200 + index * 200; y: 200 + + Text { + anchors.centerIn: parent + color: "white" + text: anim.velocity.x.toFixed(2) + "," + anim.velocity.y.toFixed(2) + } + + MomentumAnimation { id: anim; target: ball } + + DragHandler { + id: dragHandler + objectName: "dragHandler" + index + onActiveChanged: { + if (!active) + anim.restart(point.velocity) + } + } + Rectangle { + visible: dragHandler.active + anchors.fill: parent + anchors.margins: -5 + radius: width / 2 + opacity: 0.25 + } + } + } +} diff --git a/tests/manual/pointer/joystick.qml b/tests/manual/pointer/joystick.qml new file mode 100644 index 0000000000..bcc4564471 --- /dev/null +++ b/tests/manual/pointer/joystick.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + width: 480 + height: 480 + color: "black" + + Image { + id: knob + source: "resources/redball.png" + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + DragHandler { + id: dragHandler + } + states: [ + State { + when: dragHandler.active + AnchorChanges { + target: knob + anchors.horizontalCenter: undefined + anchors.verticalCenter: undefined + } + } + ] + transitions: [ + Transition { + AnchorAnimation { easing.type: Easing.OutElastic } + } + ] + } +} diff --git a/tests/manual/pointer/main.cpp b/tests/manual/pointer/main.cpp new file mode 100644 index 0000000000..7935b4072c --- /dev/null +++ b/tests/manual/pointer/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQuickItem> +#include <QQuickWindow> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (!app.arguments().isEmpty()) { + QQuickWindow * win = static_cast<QQuickWindow *>(engine.rootObjects().first()); + auto lastArg = app.arguments().last(); + if (lastArg.endsWith(QLatin1String(".qml"))) { + auto root = win->findChild<QQuickItem *>("LauncherList"); + int showExampleIdx = -1; + for (int i = root->metaObject()->methodCount(); showExampleIdx < 0 && i >= 0; --i) + if (root->metaObject()->method(i).name() == QByteArray("showExample")) + showExampleIdx = i; + QMetaMethod showExampleFn = root->metaObject()->method(showExampleIdx); + showExampleFn.invoke(root, Q_ARG(QVariant, QVariant(QLatin1String("../../") + lastArg))); + } + } + + return app.exec(); +} diff --git a/tests/manual/pointer/main.qml b/tests/manual/pointer/main.qml new file mode 100644 index 0000000000..df34c7d4a3 --- /dev/null +++ b/tests/manual/pointer/main.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtQuick.Window 2.2 +import "qrc:/quick/shared/" as Examples + +Window { + width: 800 + height: 600 + visible: true + Examples.LauncherList { + id: ll + objectName: "LauncherList" + anchors.fill: parent + Component.onCompleted: { + addExample("single point handler", "QQuickPointerSingleHandler: test properties copied from events", Qt.resolvedUrl("singlePointHandlerProperties.qml")) + addExample("joystick", "DragHandler: move one item inside another with any pointing device", Qt.resolvedUrl("joystick.qml")) + addExample("mixer", "mixing console", Qt.resolvedUrl("mixer.qml")) + addExample("pinch", "PinchHandler: scale, rotate and drag", Qt.resolvedUrl("pinchHandler.qml")) + addExample("map", "scale and pan", Qt.resolvedUrl("map.qml")) + addExample("custom map", "scale and pan", Qt.resolvedUrl("map2.qml")) + addExample("fling animation", "DragHandler: after dragging, use an animation to simulate momentum", Qt.resolvedUrl("flingAnimation.qml")) + addExample("fake Flickable", "implementation of a simplified Flickable using only Items, DragHandler and MomentumAnimation", Qt.resolvedUrl("fakeFlickable.qml")) + addExample("photo surface", "re-implementation of the existing photo surface demo using Handlers", Qt.resolvedUrl("photosurface.qml")) + addExample("tap", "TapHandler: device-agnostic tap/click detection for buttons", Qt.resolvedUrl("tapHandler.qml")) + addExample("multibuttons", "TapHandler: gesturePolicy (99 red balloons)", Qt.resolvedUrl("multibuttons.qml")) + addExample("flickable with Handlers", "Flickable with buttons, sliders etc. implemented in various ways", Qt.resolvedUrl("flickableWithHandlers.qml")) + } + } +} diff --git a/tests/manual/pointer/map.qml b/tests/manual/pointer/map.qml new file mode 100644 index 0000000000..e1ca889064 --- /dev/null +++ b/tests/manual/pointer/map.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + width: 640 + height: 480 + + Rectangle { + id: map + color: "aqua" + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: image.implicitWidth + height: image.implicitHeight + + Image { + id: image + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + source: "resources/map.svgz" + } + } + + PinchHandler { + id: pinch + target: map + minimumScale: 0.1 + maximumScale: 10 + } + + DragHandler { + target: map + } +} diff --git a/tests/manual/pointer/map2.qml b/tests/manual/pointer/map2.qml new file mode 100644 index 0000000000..fcd144bd7f --- /dev/null +++ b/tests/manual/pointer/map2.qml @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + width: 640 + height: 480 + + Rectangle { + id: map + color: "aqua" + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: image.implicitWidth + height: image.implicitHeight + property point center : Qt.point(x + map.width/2, y + map.height/2) + + function setCenter(xx, yy) { + map.x = xx - map.width/2 + map.y = yy - map.height/2 + } + + + Image { + id: image + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + source: "resources/map.svgz" + } + } + + PinchHandler { + id: pinch + target: map + minimumScale: 0.1 + maximumScale: 10 + } + + DragHandler { + property point startDrag + target: null + onActiveChanged: { + if (active) + startDrag = map.center + } + + onTranslationChanged: { + if (!target) + map.setCenter(startDrag.x + translation.x, startDrag.y + translation.y) + } + } +} diff --git a/tests/manual/pointer/mixer.qml b/tests/manual/pointer/mixer.qml new file mode 100644 index 0000000000..84ad975340 --- /dev/null +++ b/tests/manual/pointer/mixer.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 +import "content" + +Rectangle { + id: root + width: 1280 + height: 960 + objectName: "root" + color: "#222222" + + ListView { + id: list + objectName: "listView" + anchors.fill: parent + anchors.margins: 10 + orientation: Qt.Horizontal + + model: 20 + + delegate: Item { + objectName: "delegateItem" + index + width: 154 + height: list.height + + Slider { + anchors.fill: parent + label: "Channel " + (index + 1) + } + } + } +} diff --git a/tests/manual/pointer/multibuttons.qml b/tests/manual/pointer/multibuttons.qml new file mode 100644 index 0000000000..748ec87c86 --- /dev/null +++ b/tests/manual/pointer/multibuttons.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Particles 2.0 +import QtQuick.Layouts 1.0 +import Qt.labs.handlers 1.0 +import "content" + +Item { + width: 800 + height: 800 + ColumnLayout { + anchors.right: parent.right + spacing: 20 + Text { text: "protagonist"; font.pointSize: 12 } + MultiButton { + id: balloonsButton + label: "Launch Balloons" + Layout.fillWidth: true + gesturePolicy: TapHandler.WithinBounds + } + Text { text: "the goons"; font.pointSize: 12 } + MultiButton { + id: missilesButton + label: "Launch Missile" + Layout.fillWidth: true + gesturePolicy: TapHandler.ReleaseWithinBounds + onTapped: missileEmitter.burst(1) + } + MultiButton { + id: fightersButton + label: "Launch Fighters" + Layout.fillWidth: true + gesturePolicy: TapHandler.DragThreshold + } + } + ParticleSystem { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.leftMargin: 150 + ImageParticle { source: "resources/balloon.png" } + Emitter { anchors.bottom: parent.bottom; enabled: balloonsButton.pressed; lifeSpan: 5000; size: 64 + maximumEmitted: 99 + emitRate: 50; velocity: PointDirection { x: 10; y: -150; yVariation: 30; xVariation: 50 } } } + ParticleSystem { + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + ImageParticle { source: "resources/fighter.png" } + Emitter { anchors.bottom: parent.bottom; enabled: fightersButton.pressed; lifeSpan: 15000; size: 204 + emitRate: 3; velocity: PointDirection { x: -1000; y: -250; yVariation: 150; xVariation: 50 } } } + ParticleSystem { + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.rightMargin: 100 + ImageParticle { source: "resources/missile.png"; autoRotation: true; rotation: 90 } + Emitter { id: missileEmitter; anchors.bottom: parent.bottom; lifeSpan: 5000; size: 128; + emitRate: 0; velocity: PointDirection { x: -200; y: -350; yVariation: 200; xVariation: 100 } } } +} diff --git a/tests/manual/pointer/photosurface.qml b/tests/manual/pointer/photosurface.qml new file mode 100644 index 0000000000..a2e8b2aede --- /dev/null +++ b/tests/manual/pointer/photosurface.qml @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtQuick.Dialogs 1.0 +import Qt.labs.folderlistmodel 1.0 +import Qt.labs.handlers 1.0 +import "content" + +Rectangle { + id: root + visible: true + width: 1024; height: 600 + color: "black" + property int highestZ: 0 + property real defaultSize: 200 + property real surfaceViewportRatio: 1.5 + + FileDialog { + id: fileDialog + title: "Choose a folder with some images" + selectFolder: true + onAccepted: folderModel.folder = fileUrl + "/" + } + Shortcut { + id: openShortcut + sequence: StandardKey.Open + onActivated: fileDialog.open() + } + + FakeFlickable { + id: flick + anchors.fill: parent + contentWidth: width * 2 + contentHeight: height * 2 + Repeater { + model: FolderListModel { + id: folderModel + folder: "resources/" + objectName: "folderModel" + showDirs: false + nameFilters: ["*.png", "*.jpg", "*.gif"] + } + Rectangle { + id: photoFrame + objectName: "frame-" + fileName + width: image.width * (1 + 0.10 * image.height / image.width) + height: image.height * 1.10 + scale: defaultSize / Math.max(image.sourceSize.width, image.sourceSize.height) + Behavior on scale { NumberAnimation { duration: 200 } } + Behavior on x { NumberAnimation { duration: 200 } } + Behavior on y { NumberAnimation { duration: 200 } } + border.color: pinchHandler.active || dragHandler.active ? "red" : "black" + border.width: 2 + smooth: true + antialiasing: true + Component.onCompleted: { + x = Math.random() * root.width - width / 2 + y = Math.random() * root.height - height / 2 + rotation = Math.random() * 13 - 6 + } + Image { + id: image + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + source: folderModel.folder + fileName + antialiasing: true + } + + MomentumAnimation { id: anim; target: photoFrame } + + DragHandler { + id: dragHandler + onActiveChanged: { + if (!active) + anim.restart(point.velocity) + } + } + + PinchHandler { + id: pinchHandler + minimumRotation: -360 + maximumRotation: 360 + minimumScale: 0.1 + maximumScale: 10 + property real zRestore: 0 + } + } + } + } + + Rectangle { + id: verticalScrollDecorator + anchors.right: parent.right + anchors.margins: 2 + color: "cyan" + border.color: "black" + border.width: 1 + width: 5 + radius: 2 + antialiasing: true + height: flick.height * (flick.height / flick.contentHeight) - (width - anchors.margins) * 2 + y: -flick.contentY * (flick.height / flick.contentHeight) + NumberAnimation on opacity { id: vfade; to: 0; duration: 500 } + onYChanged: { opacity = 1.0; fadeTimer.restart() } + } + + Rectangle { + id: horizontalScrollDecorator + anchors.bottom: parent.bottom + anchors.margins: 2 + color: "cyan" + border.color: "black" + border.width: 1 + height: 5 + radius: 2 + antialiasing: true + width: flick.width * (flick.width / flick.contentWidth) - (height - anchors.margins) * 2 + x: -flick.contentX * (flick.width / flick.contentWidth) + NumberAnimation on opacity { id: hfade; to: 0; duration: 500 } + onXChanged: { opacity = 1.0; fadeTimer.restart() } + } + + Timer { id: fadeTimer; interval: 1000; onTriggered: { hfade.start(); vfade.start() } } + + Text { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 10 + color: "darkgrey" + wrapMode: Text.WordWrap + font.pointSize: 8 + text: "Press " + openShortcut.nativeText + " to choose a different image folder\n" + + "On a touchscreen: use two fingers to zoom and rotate, one finger to drag\n" + + "With a mouse: drag normally" + } +} diff --git a/tests/manual/pointer/pinchDragFlingMPTA.qml b/tests/manual/pointer/pinchDragFlingMPTA.qml new file mode 100644 index 0000000000..6446a17873 --- /dev/null +++ b/tests/manual/pointer/pinchDragFlingMPTA.qml @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 +import "content" + +Rectangle { + width: 1024; height: 600 + color: "beige" + objectName: "beige root" + + function getTransformationDetails(item, pinchhandler) { + return "scale:" + pinchhandler.scale.toFixed(2) + + " rotation:" + pinchhandler.rotation.toFixed(2) + + " translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")" + } + + Rectangle { + id: container + objectName: "container rect" + width: 600 + height: 500 + color: pinch3.active ? "red" : "black" + antialiasing: true + Loader { + source: "../touch/mpta-crosshairs.qml" + anchors.fill: parent + anchors.margins: 2 + } + Item { + anchors.fill: parent + // In order for PinchHandler to get a chance to take a passive grab, it has to get the touchpoints first. + // In order to get the touchpoints first, it has to be on top of the Z order: i.e. come last in paintOrderChildItems(). + // This is the opposite situation as with filtersChildMouseEvents: e.g. PinchArea would have wanted to be the parent, + // if it even knew that trick (which it doesn't). + PinchHandler { + id: pinch3 + objectName: "3-finger pinch" + target: container + requiredPointCount: 3 + minimumScale: 0.1 + maximumScale: 10 + onActiveChanged: if (!active) fling.restart(centroidVelocity) + } + DragHandler { + id: dragHandler + objectName: "DragHandler" + target: container + acceptedModifiers: Qt.MetaModifier + onActiveChanged: if (!active) fling.restart(point.velocity) + } + } + MomentumAnimation { id: fling; target: container } + } + Text { + anchors.bottom: parent.bottom + text: pinch3.active ? getTransformationDetails(container, pinch3) : "Pinch with 3 fingers to scale, rotate and translate" + } +} diff --git a/tests/manual/pointer/pinchHandler.qml b/tests/manual/pointer/pinchHandler.qml new file mode 100644 index 0000000000..71cdd98e4f --- /dev/null +++ b/tests/manual/pointer/pinchHandler.qml @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 +import "content" + +Rectangle { + width: 1024; height: 600 + color: "#eee" + + function getTransformationDetails(item, pinchhandler) { + return "\n\npinch.scale:" + pinchhandler.scale.toFixed(2) + + "\npinch.rotation:" + pinchhandler.rotation.toFixed(2) + + "\npinch.translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")" + + "\nrect.scale: " + item.scale.toFixed(2) + + "\nrect.rotation: " + item.rotation.toFixed(2) + + "\nrect.position: " + "(" + item.x.toFixed(2) + "," + item.y.toFixed(2) + ")" + } + + Rectangle { + // Purpose of this item is just to make sure the rectangles are transformed into + // a coordinate system that is different from the scene coordinate system. + anchors.fill: parent + anchors.margins: 50 + color: "#ffe0e0e0" + + Rectangle { + id: rect2 + width: 400 + height: 300 + color: "lightsteelblue" + antialiasing: true + x: 100 + y: 200 + rotation: 30 + transformOrigin: Item.TopRight + border.width: pinch2.active ? 2 : 0 + border.color: pinch2.active ? "red" : "transparent" + + Text { + anchors.centerIn: parent + text: "Pinch with 2 fingers to scale, rotate and translate" + + getTransformationDetails(rect2, pinch2) + } + + PinchHandler { + id: pinch2 + objectName: "2-finger pinch" + minimumRotation: -45 + maximumRotation: 45 + minimumScale: 0.5 + maximumScale: 3 + minimumX: 0 + maximumX: 600 + pointDistanceThreshold: 0 + // acceptedModifiers: Qt.ControlModifier + } + } + + Rectangle { + id: rect3 + x: 500 + width: 400 + height: 300 + color: "wheat" + antialiasing: true + border.width: (dragHandler.active || pinch3.active) ? 2 : 0 + border.color: border.width > 0 ? "red" : "transparent" + + Text { + anchors.centerIn: parent + text: "Pinch with 3 fingers to scale, rotate and translate\nDrag with 1 finger" + + getTransformationDetails(rect3, pinch3) + } + DragHandler { + id: dragHandler + objectName: "DragHandler" + } + + PinchHandler { + id: pinch3 + objectName: "3-finger pinch" + requiredPointCount: 3 + minimumScale: 0.1 + maximumScale: 10 + onActiveChanged: { + if (!active) + anim.restart(centroidVelocity) + } + } + + MomentumAnimation { id: anim; target: rect3 } + } + } + Rectangle { + id: centroidIndicator + property QtObject pincher: pinch2.active ? pinch2 : pinch3 + x: pincher.centroid.x - radius + y: pincher.centroid.y - radius + z: 1 + visible: pincher.active + radius: width / 2 + width: 10 + height: width + color: "red" + } +} diff --git a/tests/manual/pointer/pointer.pro b/tests/manual/pointer/pointer.pro new file mode 100644 index 0000000000..3705d41df0 --- /dev/null +++ b/tests/manual/pointer/pointer.pro @@ -0,0 +1,7 @@ +TEMPLATE = app + +QT += qml quick + +SOURCES += main.cpp + +RESOURCES += qml.qrc ../../../examples/quick/shared/quick_shared.qrc diff --git a/tests/manual/pointer/qml.qrc b/tests/manual/pointer/qml.qrc new file mode 100644 index 0000000000..68937a8c4a --- /dev/null +++ b/tests/manual/pointer/qml.qrc @@ -0,0 +1,39 @@ +<RCC> + <qresource prefix="/"> + <file>flingAnimation.qml</file> + <file>main.qml</file> + <file>fakeFlickable.qml</file> + <file>flickableWithHandlers.qml</file> + <file>joystick.qml</file> + <file>map.qml</file> + <file>mixer.qml</file> + <file>multibuttons.qml</file> + <file>photosurface.qml</file> + <file>pinchHandler.qml</file> + <file>singlePointHandlerProperties.qml</file> + <file>tapHandler.qml</file> + <file>content/FakeFlickable.qml</file> + <file>content/FlashAnimation.qml</file> + <file>content/MomentumAnimation.qml</file> + <file>content/MouseAreaButton.qml</file> + <file>content/MouseAreaSlider.qml</file> + <file>content/MptaButton.qml</file> + <file>content/MultiButton.qml</file> + <file>content/ScrollBar.qml</file> + <file>content/Slider.qml</file> + <file>content/TapHandlerButton.qml</file> + <file>resources/arrowhead.png</file> + <file>resources/balloon.png</file> + <file>resources/fighter.png</file> + <file>resources/grabbing-location.svg</file> + <file>resources/map.svgz</file> + <file>resources/missile.png</file> + <file>resources/mixer-knob.png</file> + <file>resources/mouse.png</file> + <file>resources/mouse_left.png</file> + <file>resources/mouse_middle.png</file> + <file>resources/mouse_right.png</file> + <file>resources/redball.png</file> + <file>map2.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/pointer/resources/arrowhead.png b/tests/manual/pointer/resources/arrowhead.png Binary files differnew file mode 100644 index 0000000000..7719bc6d6a --- /dev/null +++ b/tests/manual/pointer/resources/arrowhead.png diff --git a/tests/manual/pointer/resources/balloon.png b/tests/manual/pointer/resources/balloon.png Binary files differnew file mode 100644 index 0000000000..6476facc49 --- /dev/null +++ b/tests/manual/pointer/resources/balloon.png diff --git a/tests/manual/pointer/resources/fighter.png b/tests/manual/pointer/resources/fighter.png Binary files differnew file mode 100644 index 0000000000..2acee43cba --- /dev/null +++ b/tests/manual/pointer/resources/fighter.png diff --git a/tests/manual/pointer/resources/grabbing-location.svg b/tests/manual/pointer/resources/grabbing-location.svg new file mode 100644 index 0000000000..c26881e9ba --- /dev/null +++ b/tests/manual/pointer/resources/grabbing-location.svg @@ -0,0 +1 @@ +<svg width="3408" height="3124"><path d="M1517 1562c0-126-93-229-208-229s-208 102-208 229c0 126 93 229 208 229s208-102 208-229zm123-172c-58-206-221-365-424-412l-270 531H380l203-223 219-241 346-380c42 14 82 32 121 54 232 128 402 375 449 671h-77zm-551-933c448 123 782 546 802 1055h1517C3386 673 2787 0 2050 0H696L0 1367h120l826-938 146 25c-1 1-2 2-3 4zm551 1277c-58 206-221 365-424 412l-270-531H380l203 223 219 241 346 380c42-14 82-32 121-54 232-128 402-375 449-671h-77zm-548 936l-146 25-826-938H0l696 1367h1354c737 0 1337-673 1358-1512H1891c-19 509-354 933-802 1055 1 1 2 2 3 4z" fill="#ee832b"/></svg> diff --git a/tests/manual/pointer/resources/map.svgz b/tests/manual/pointer/resources/map.svgz Binary files differnew file mode 100644 index 0000000000..64d509c106 --- /dev/null +++ b/tests/manual/pointer/resources/map.svgz diff --git a/tests/manual/pointer/resources/missile.png b/tests/manual/pointer/resources/missile.png Binary files differnew file mode 100644 index 0000000000..72c02d1fb9 --- /dev/null +++ b/tests/manual/pointer/resources/missile.png diff --git a/tests/manual/pointer/resources/mixer-knob.png b/tests/manual/pointer/resources/mixer-knob.png Binary files differnew file mode 100644 index 0000000000..b7d42ee3bd --- /dev/null +++ b/tests/manual/pointer/resources/mixer-knob.png diff --git a/tests/manual/pointer/resources/mouse.png b/tests/manual/pointer/resources/mouse.png Binary files differnew file mode 100644 index 0000000000..268946df0a --- /dev/null +++ b/tests/manual/pointer/resources/mouse.png diff --git a/tests/manual/pointer/resources/mouse_left.png b/tests/manual/pointer/resources/mouse_left.png Binary files differnew file mode 100644 index 0000000000..9292301b47 --- /dev/null +++ b/tests/manual/pointer/resources/mouse_left.png diff --git a/tests/manual/pointer/resources/mouse_middle.png b/tests/manual/pointer/resources/mouse_middle.png Binary files differnew file mode 100644 index 0000000000..064e8b9c16 --- /dev/null +++ b/tests/manual/pointer/resources/mouse_middle.png diff --git a/tests/manual/pointer/resources/mouse_right.png b/tests/manual/pointer/resources/mouse_right.png Binary files differnew file mode 100644 index 0000000000..cab1a36ba6 --- /dev/null +++ b/tests/manual/pointer/resources/mouse_right.png diff --git a/tests/manual/pointer/resources/redball.png b/tests/manual/pointer/resources/redball.png Binary files differnew file mode 100644 index 0000000000..68d2e1d638 --- /dev/null +++ b/tests/manual/pointer/resources/redball.png diff --git a/tests/manual/pointer/singlePointHandlerProperties.qml b/tests/manual/pointer/singlePointHandlerProperties.qml new file mode 100644 index 0000000000..f91094ee9e --- /dev/null +++ b/tests/manual/pointer/singlePointHandlerProperties.qml @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + width: 480 + height: 480 + color: "black" + + Item { + id: crosshairs + x: dragHandler.point.position.x - width / 2 + y: dragHandler.point.position.y - height / 2 + width: parent.width / 2; height: parent.height / 2 + visible: dragHandler.active + rotation: dragHandler.point.rotation + + Rectangle { + color: "goldenrod" + anchors.centerIn: parent + width: 2; height: parent.height + antialiasing: true + } + Rectangle { + color: "goldenrod" + anchors.centerIn: parent + width: parent.width; height: 2 + antialiasing: true + } + Rectangle { + color: "goldenrod" + width: Math.max(2, 50 * dragHandler.point.pressure) + height: width + radius: width / 2 + anchors.centerIn: parent + antialiasing: true + Rectangle { + y: -40 + anchors.horizontalCenter: parent.horizontalCenter + color: "lightsteelblue" + implicitWidth: label.implicitWidth + implicitHeight: label.implicitHeight + Text { + id: label + text: 'id: ' + dragHandler.point.id.toString(16) + " uid: " + dragHandler.point.uniqueId.numericId + + '\npos: (' + dragHandler.point.position.x.toFixed(2) + ', ' + dragHandler.point.position.y.toFixed(2) + ')' + } + } + } + Rectangle { + color: "transparent" + border.color: "white" + antialiasing: true + width: dragHandler.point.ellipseDiameters.width + height: dragHandler.point.ellipseDiameters.height + radius: Math.min(width / 2, height / 2) + anchors.centerIn: parent + } + } + Rectangle { + id: velocityVector + visible: width > 0 + width: dragHandler.point.velocity.length() * 100 + height: 2 + x: dragHandler.point.position.x + y: dragHandler.point.position.y + rotation: Math.atan2(dragHandler.point.velocity.y, dragHandler.point.velocity.x) * 180 / Math.PI + transformOrigin: Item.BottomLeft + antialiasing: true + + Image { + source: "resources/arrowhead.png" + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + width: 16 + height: 12 + antialiasing: true + } + } + + Component { + id: grabbingLocationIndicator + Image { + source: "resources/grabbing-location.svg" + sourceSize.width: 32 + sourceSize.height: 32 + } + } + + Component { + id: mouseButtonIndicator + Image { + property int buttons + source: "resources/mouse.png" + Image { + source: "resources/mouse_left.png" + visible: buttons & Qt.LeftButton + } + Image { + source: "resources/mouse_middle.png" + visible: buttons & Qt.MidButton + } + Image { + source: "resources/mouse_right.png" + visible: buttons & Qt.RightButton + } + } + } + + DragHandler { + id: dragHandler + target: null + onGrabChanged: if (active) { // 'point' is an implicit parameter referencing to a QQuickEventPoint instance + console.log("grabbed " + point.pointId + " @ " + point.sceneGrabPos) + grabbingLocationIndicator.createObject(root, {"x": point.sceneGrabPos.x, "y": point.sceneGrabPos.y - 16}) + } + onPointChanged: { + // Here, 'point' is referring to the property of the DragHandler + if (point.pressedButtons) + mouseButtonIndicator.createObject(root, {"x": point.pressPosition.x - 44, "y": point.pressPosition.y - 64, "buttons": point.pressedButtons}) + } + } +} diff --git a/tests/manual/pointer/tapHandler.qml b/tests/manual/pointer/tapHandler.qml new file mode 100644 index 0000000000..5dac99a899 --- /dev/null +++ b/tests/manual/pointer/tapHandler.qml @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 +import "qrc:/quick/shared/" as Examples + +Item { + width: 480 + height: 320 + + Rectangle { + id: rect + anchors.fill: parent; anchors.margins: 40 + border.width: 3; border.color: "transparent" + color: handler.isPressed ? "lightsteelblue" : "darkgrey" + + TapHandler { + id: handler + acceptedButtons: (leftAllowedCB.checked ? Qt.LeftButton : Qt.NoButton) | + (middleAllowedCB.checked ? Qt.MiddleButton : Qt.NoButton) | + (rightAllowedCB.checked ? Qt.RightButton : Qt.NoButton) + gesturePolicy: (policyDragThresholdCB.checked ? TapHandler.DragThreshold : + policyWithinBoundsCB.checked ? TapHandler.WithinBounds : + TapHandler.ReleaseWithinBounds) + + onCanceled: { + console.log("canceled @ " + point.position) + borderBlink.blinkColor = "red" + borderBlink.start() + } + onTapped: { // 'point' is an implicit parameter referencing to a QQuickEventPoint instance + console.log("tapped @ " + point.pos + " button(s) " + point.event.button + " tapCount " + tapCount) + if (tapCount > 1) { + tapCountLabel.text = tapCount + flashAnimation.start() + } else { + borderBlink.start() + } + } + onLongPressed: longPressFeedback.createObject(rect, + {"x": point.position.x, "y": point.position.y, + "text": Math.round(handler.timeHeld).toFixed(3) + " sec", + "color": borderBlink.blinkColor}) + } + + Text { + id: tapCountLabel + anchors.centerIn: parent + font.pixelSize: 72 + font.weight: Font.Black + SequentialAnimation { + id: flashAnimation + PropertyAction { target: tapCountLabel; property: "visible"; value: true } + PropertyAction { target: tapCountLabel; property: "opacity"; value: 1.0 } + PropertyAction { target: tapCountLabel; property: "scale"; value: 1.0 } + ParallelAnimation { + NumberAnimation { + target: tapCountLabel + property: "opacity" + to: 0 + duration: 500 + } + NumberAnimation { + target: tapCountLabel + property: "scale" + to: 1.5 + duration: 500 + } + } + } + } + + Rectangle { + id: expandingCircle + radius: handler.timeHeld * 100 + visible: radius > 0 && handler.isPressed + border.width: 3 + border.color: borderBlink.blinkColor + color: "transparent" + width: radius * 2 + height: radius * 2 + x: handler.point.pressPosition.x - radius + y: handler.point.pressPosition.y - radius + opacity: 0.25 + } + + Component { + id: longPressFeedback + Text { } + } + + SequentialAnimation { + id: borderBlink + property color blinkColor: (function(pbtns) { + switch (pbtns) { + case Qt.MiddleButton: return "orange"; + case Qt.RightButton: return "magenta"; + default: return "green"; + } + })(handler.point.pressedButtons) + loops: 3 + ScriptAction { script: rect.border.color = borderBlink.blinkColor } + PauseAnimation { duration: 100 } + ScriptAction { script: rect.border.color = "transparent" } + PauseAnimation { duration: 100 } + } + } + + Row { + spacing: 6 + Text { text: "accepted mouse clicks:"; anchors.verticalCenter: leftAllowedCB.verticalCenter } + Examples.CheckBox { + id: leftAllowedCB + checked: true + text: "left click" + } + Examples.CheckBox { + id: middleAllowedCB + text: "middle click" + } + Examples.CheckBox { + id: rightAllowedCB + text: "right click" + } + Text { text: " gesture policy:"; anchors.verticalCenter: leftAllowedCB.verticalCenter } + Examples.CheckBox { + id: policyDragThresholdCB + text: "drag threshold" + onCheckedChanged: if (checked) { + policyWithinBoundsCB.checked = false; + policyReleaseWithinBoundsCB.checked = false; + } + } + Examples.CheckBox { + id: policyWithinBoundsCB + text: "within bounds" + onCheckedChanged: if (checked) { + policyDragThresholdCB.checked = false; + policyReleaseWithinBoundsCB.checked = false; + } + } + Examples.CheckBox { + id: policyReleaseWithinBoundsCB + checked: true + text: "release within bounds" + onCheckedChanged: if (checked) { + policyDragThresholdCB.checked = false; + policyWithinBoundsCB.checked = false; + } + } + } +} diff --git a/tests/manual/pointer/tapWithModifiers.qml b/tests/manual/pointer/tapWithModifiers.qml new file mode 100644 index 0000000000..9a6977da1c --- /dev/null +++ b/tests/manual/pointer/tapWithModifiers.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Item { + width: 200 + height: 200 + TapHandler { + acceptedModifiers: Qt.ControlModifier + onTapped: console.log("control-tapped") + } + TapHandler { + acceptedModifiers: Qt.NoModifier + onTapped: console.log("tapped with no modifiers") + } + TapHandler { + onTapped: console.log("tapped with modifiers " + point.event.modifiers) + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_arc.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_arc.qml new file mode 100644 index 0000000000..0b2396012e --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_arc.qml @@ -0,0 +1,112 @@ +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Item { + width: 320 + height: 480 + + Column { + Item { + width: 200 + height: 100 + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "transparent" + strokeColor: model.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + startX: 4; startY: 4 + PathArc { + id: arc + x: 96; y: 96 + radiusX: 100; radiusY: 100 + direction: model.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise + } + } + } + } + } + + Item { + width: 200 + height: 100 + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "transparent" + strokeColor: model.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + startX: 50; startY: 100 + PathArc { + x: 100; y: 150 + radiusX: 50; radiusY: 50 + useLargeArc: model.index === 1 + } + } + } + } + } + + Item { + width: 200 + height: 100 + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "transparent" + strokeColor: model.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + startX: 50; startY: 150 + PathArc { + x: 150; y: 150 + radiusX: 50; radiusY: 20 + xAxisRotation: model.index === 0 ? 0 : 45 + } + } + } + } + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "transparent" + strokeColor: model.index === 0 ? "red" : "blue" + + startX: 50; startY: 150 + PathArc { + x: 150; y: 150 + radiusX: 50; radiusY: 20 + xAxisRotation: model.index === 0 ? 0 : 45 + direction: PathArc.Counterclockwise + } + } + } + } + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_arc_fill.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_arc_fill.qml new file mode 100644 index 0000000000..fefc2ec3eb --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_arc_fill.qml @@ -0,0 +1,112 @@ +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Item { + width: 320 + height: 480 + + Column { + Item { + width: 200 + height: 100 + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "lightBlue" + strokeColor: model.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + startX: 4; startY: 4 + PathArc { + id: arc + x: 96; y: 96 + radiusX: 100; radiusY: 100 + direction: model.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise + } + } + } + } + } + + Item { + width: 200 + height: 100 + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "green" + strokeColor: model.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + startX: 50; startY: 100 + PathArc { + x: 100; y: 150 + radiusX: 50; radiusY: 50 + useLargeArc: model.index === 1 + } + } + } + } + } + + Item { + width: 200 + height: 100 + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "gray" + strokeColor: model.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + startX: 50; startY: 150 + PathArc { + x: 150; y: 150 + radiusX: 50; radiusY: 20 + xAxisRotation: model.index === 0 ? 0 : 45 + } + } + } + } + + Repeater { + model: 2 + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + fillColor: "lightGray" + strokeColor: model.index === 0 ? "red" : "blue" + + startX: 50; startY: 150 + PathArc { + x: 150; y: 150 + radiusX: 50; radiusY: 20 + xAxisRotation: model.index === 0 ? 0 : 45 + direction: PathArc.Counterclockwise + } + } + } + } + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_cubic.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_cubic.qml new file mode 100644 index 0000000000..1d2f9fd40d --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_cubic.qml @@ -0,0 +1,35 @@ +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Item { + width: 320 + height: 480 + + Column { + Repeater { + model: 4 + Item { + width: 200 + height: 100 + + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + strokeWidth: (model.index + 2) * 2 + strokeColor: "black" + fillColor: "lightBlue" + + startX: 50; startY: 100 + PathCubic { + x: 150; y: 100 + control1X: model.index * 10; control1Y: model.index * 5 + control2X: model.index * -10; control2Y: model.index * 10 + } + } + } + } + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_linear_gradient.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_linear_gradient.qml new file mode 100644 index 0000000000..1caaec7781 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_linear_gradient.qml @@ -0,0 +1,33 @@ +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Item { + width: 320 + height: 480 + + Shape { + vendorExtensionsEnabled: false + + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_lines.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_lines.qml new file mode 100644 index 0000000000..56045cb5ae --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_lines.qml @@ -0,0 +1,92 @@ +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Item { + width: 320 + height: 480 + + Shape { + vendorExtensionsEnabled: false + + anchors.fill: parent + + ShapePath { + strokeWidth: 1 + strokeColor: "red" + fillColor: "transparent" + PathLine { x: 50; y: 50 } + } + ShapePath { + strokeWidth: 2 + strokeColor: "blue" + fillColor: "transparent" + startX: 20 + PathLine { x: 70; y: 50 } + } + ShapePath { + strokeWidth: 3 + strokeColor: "green" + fillColor: "transparent" + startX: 40 + PathLine { x: 90; y: 50 } + } + ShapePath { + strokeWidth: 4 + strokeColor: "yellow" + fillColor: "transparent" + startX: 60 + PathLine { x: 110; y: 50 } + } + ShapePath { + strokeWidth: 5 + strokeColor: "black" + fillColor: "transparent" + strokeStyle: ShapePath.DashLine + startX: 80 + PathLine { x: 130; y: 50 } + } + + ShapePath { + strokeWidth: 20 + strokeColor: "gray" + fillColor: "transparent" + capStyle: ShapePath.RoundCap + startX: 120; startY: 20 + PathLine { x: 200; y: 100 } + } + + ShapePath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + joinStyle: ShapePath.BevelJoin + startX: 20 + startY: 100 + PathLine { x: 120; y: 200 } + PathLine { x: 50; y: 200 } + } + ShapePath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + joinStyle: ShapePath.MiterJoin + startX: 150 + startY: 100 + PathLine { x: 250; y: 200 } + PathLine { x: 180; y: 200 } + } + ShapePath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + joinStyle: ShapePath.RoundJoin + startX: 270 + startY: 100 + PathLine { x: 310; y: 200 } + PathLine { x: 280; y: 200 } + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_quad.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_quad.qml new file mode 100644 index 0000000000..a4c95f7c15 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_quad.qml @@ -0,0 +1,34 @@ +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Item { + width: 320 + height: 480 + + Column { + Repeater { + model: 4 + Item { + width: 200 + height: 100 + + Shape { + anchors.fill: parent + vendorExtensionsEnabled: false + + ShapePath { + strokeWidth: (model.index + 2) * 2 + strokeColor: "black" + fillColor: "lightBlue" + + startX: 50; startY: 100 + PathQuad { + x: 150; y: 100 + controlX: model.index * 10; controlY: model.index * 5 + } + } + } + } + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_spread.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_spread.qml new file mode 100644 index 0000000000..f310f08773 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_spread.qml @@ -0,0 +1,35 @@ +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Item { + width: 320 + height: 480 + + Column { + Repeater { + model: 3 + Shape { + vendorExtensionsEnabled: false + width: 200 + height: 150 + ShapePath { + strokeColor: "transparent" + + fillGradient: LinearGradient { + id: grad + y1: 50; y2: 80 + spread: model.index === 0 ? ShapeGradient.PadSpread : (model.index === 1 ? ShapeGradient.RepeatSpread : ShapeGradient.ReflectSpread) + GradientStop { position: 0; color: "black" } + GradientStop { position: 1; color: "red" } + } + + startX: 10; startY: 10 + PathLine { relativeX: 180; relativeY: 0 } + PathLine { relativeX: 0; relativeY: 100 } + PathLine { relativeX: -180; relativeY: 0 } + PathLine { relativeX: 0; relativeY: -100 } + } + } + } + } +} diff --git a/tests/manual/scenegraph_lancelot/data/text/text_advance_bidi_ltr.qml b/tests/manual/scenegraph_lancelot/data/text/text_advance_bidi_ltr.qml new file mode 100644 index 0000000000..2f40aece89 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_advance_bidi_ltr.qml @@ -0,0 +1,56 @@ +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + + property string firstWord: "One, שתיים, " + property string secondWord: "Three" + + Text { + id: referenceText + text: firstWord + secondWord + anchors.centerIn: parent + font.italic: true + font.pixelSize: 30 + } + + Text { + id: firstWordItem + anchors.left: referenceText.left + anchors.top: referenceText.bottom + text: firstWord + font: referenceText.font + } + + Text { + id: secondWordItem + anchors.left: firstWordItem.left + anchors.leftMargin: firstWordItem.advance.width + anchors.baseline: firstWordItem.baseline + anchors.baselineOffset: firstWordItem.advance.height + text: secondWord + font: referenceText.font + } + + Text { + id: firstWordItemRichText + anchors.left: referenceText.left + anchors.top: secondWordItem.bottom + text: firstWord + font: referenceText.font + textFormat: Text.RichText + } + + Text { + id: secondWordItemRichText + anchors.left: firstWordItemRichText.left + anchors.leftMargin: firstWordItemRichText.advance.width + anchors.baseline: firstWordItemRichText.baseline + anchors.baselineOffset: firstWordItemRichText.advance.height + text: secondWord + font: referenceText.font + textFormat: Text.RichText + } + +} diff --git a/tests/manual/scenegraph_lancelot/data/text/text_advance_hebrew.qml b/tests/manual/scenegraph_lancelot/data/text/text_advance_hebrew.qml new file mode 100644 index 0000000000..0a9dce4d82 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_advance_hebrew.qml @@ -0,0 +1,54 @@ +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + + property string firstWord: "תורת רב־לשוני אנא " + property string secondWord: "של" + + Text { + id: referenceText + text: firstWord + secondWord + anchors.centerIn: parent + font.italic: true + font.pixelSize: 30 + } + + Text { + id: firstWordItem + anchors.right: referenceText.right + anchors.top: referenceText.bottom + text: firstWord + font: referenceText.font + } + + Text { + id: secondWordItem + anchors.right: firstWordItem.left + anchors.baseline: firstWordItem.baseline + anchors.baselineOffset: firstWordItem.advance.height + text: secondWord + font: referenceText.font + } + + Text { + id: firstWordItemRichText + anchors.right: referenceText.right + anchors.top: secondWordItem.bottom + text: firstWord + font: referenceText.font + textFormat: Text.RichText + } + + Text { + id: secondWordItemRichText + anchors.right: firstWordItemRichText.left + anchors.baseline: firstWordItemRichText.baseline + anchors.baselineOffset: firstWordItemRichText.advance.height + text: secondWord + font: referenceText.font + textFormat: Text.RichText + } + +} diff --git a/tests/manual/scenegraph_lancelot/data/text/text_advance_latin.qml b/tests/manual/scenegraph_lancelot/data/text/text_advance_latin.qml new file mode 100644 index 0000000000..ccab5d8c64 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_advance_latin.qml @@ -0,0 +1,55 @@ +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + + property string firstWord: "Hello " + property string secondWord: "World" + + Text { + id: referenceText + text: firstWord + secondWord + anchors.centerIn: parent + font.italic: true + font.pixelSize: 30 + } + + Text { + id: firstWordItem + anchors.left: referenceText.left + anchors.top: referenceText.bottom + text: firstWord + font: referenceText.font + } + + Text { + id: secondWordItem + anchors.left: firstWordItem.left + anchors.leftMargin: firstWordItem.advance.width + anchors.baseline: firstWordItem.baseline + anchors.baselineOffset: firstWordItem.advance.height + text: secondWord + font: referenceText.font + } + + Text { + id: firstWordItemRichText + anchors.left: referenceText.left + anchors.top: secondWordItem.bottom + text: firstWord + font: referenceText.font + textFormat: Text.RichText + } + + Text { + id: secondWordItemRichText + anchors.left: firstWordItemRichText.left + anchors.leftMargin: firstWordItemRichText.advance.width + anchors.baseline: firstWordItemRichText.baseline + anchors.baselineOffset: firstWordItemRichText.advance.height + text: secondWord + font: referenceText.font + textFormat: Text.RichText + } +} diff --git a/tests/manual/scenegraph_lancelot/data/text/text_advance_multiline.qml b/tests/manual/scenegraph_lancelot/data/text/text_advance_multiline.qml new file mode 100644 index 0000000000..ae0f10718c --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_advance_multiline.qml @@ -0,0 +1,55 @@ +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + + property string firstWord: "One,\nTwo, " + property string secondWord: "Three" + + Text { + id: referenceText + text: firstWord + secondWord + anchors.centerIn: parent + font.italic: true + font.pixelSize: 30 + } + + Text { + id: firstWordItem + anchors.left: referenceText.left + anchors.top: referenceText.bottom + text: firstWord + font: referenceText.font + } + + Text { + id: secondWordItem + anchors.left: firstWordItem.left + anchors.leftMargin: firstWordItem.advance.width + anchors.baseline: firstWordItem.baseline + anchors.baselineOffset: firstWordItem.advance.height + text: secondWord + font: referenceText.font + } + + Text { + id: firstWordItemRichText + anchors.left: referenceText.left + anchors.top: secondWordItem.bottom + text: firstWord + font: referenceText.font + textFormat: Text.RichText + } + + Text { + id: secondWordItemRichText + anchors.left: firstWordItemRichText.left + anchors.leftMargin: firstWordItemRichText.advance.width + anchors.baseline: firstWordItemRichText.baseline + anchors.baselineOffset: firstWordItemRichText.advance.height + text: secondWord + font: referenceText.font + textFormat: Text.RichText + } +} diff --git a/tests/manual/scenegraph_lancelot/data/text/text_advance_multiparagraph.qml b/tests/manual/scenegraph_lancelot/data/text/text_advance_multiparagraph.qml new file mode 100644 index 0000000000..76f0910680 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_advance_multiparagraph.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + + property string firstWord: "<p>One,</p><p>Two, " + property string secondWord: "Three</p>" + + Text { + id: referenceText + text: firstWord + secondWord + anchors.centerIn: parent + font.italic: true + font.pixelSize: 30 + textFormat: Text.RichText + } + + + Text { + id: firstWordItemRichText + anchors.left: referenceText.left + anchors.top: referenceText.bottom + text: firstWord + font: referenceText.font + textFormat: Text.RichText + } + + Text { + id: secondWordItemRichText + anchors.left: firstWordItemRichText.left + anchors.leftMargin: firstWordItemRichText.advance.width + anchors.baseline: firstWordItemRichText.baseline + anchors.baselineOffset: firstWordItemRichText.advance.height + text: secondWord + font: referenceText.font + textFormat: Text.RichText + } +} diff --git a/tests/manual/scenegraph_lancelot/data/text/text_advance_multiparagraph_multifontsizes.qml b/tests/manual/scenegraph_lancelot/data/text/text_advance_multiparagraph_multifontsizes.qml new file mode 100644 index 0000000000..de33d65cdc --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_advance_multiparagraph_multifontsizes.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + + property string firstWord: "<p style=\"font-size: 40pt\">One,</p><p>Two, " + property string secondWord: "Three</p>" + + Text { + id: referenceText + text: firstWord + secondWord + anchors.centerIn: parent + font.italic: true + font.pixelSize: 30 + textFormat: Text.RichText + } + + + Text { + id: firstWordItemRichText + anchors.left: referenceText.left + anchors.top: referenceText.bottom + text: firstWord + font: referenceText.font + textFormat: Text.RichText + } + + Text { + id: secondWordItemRichText + anchors.left: firstWordItemRichText.left + anchors.leftMargin: firstWordItemRichText.advance.width + anchors.baseline: firstWordItemRichText.baseline + anchors.baselineOffset: firstWordItemRichText.advance.height + text: secondWord + font: referenceText.font + textFormat: Text.RichText + } +} diff --git a/tests/manual/scenegraph_lancelot/data/text/text_nokerning_latin.qml b/tests/manual/scenegraph_lancelot/data/text/text_nokerning_latin.qml new file mode 100644 index 0000000000..fd1d082c99 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_nokerning_latin.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + + Text { + text: "OATS FLAVOUR WAY" + anchors.centerIn: parent + + font.family: "Times New Roman" + font.italic: true + font.pixelSize: 30 + font.kerning: false + } +} diff --git a/tests/manual/shapestest/main.cpp b/tests/manual/shapestest/main.cpp new file mode 100644 index 0000000000..b9b93fbf9f --- /dev/null +++ b/tests/manual/shapestest/main.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QSurfaceFormat> +#include <QQuickView> +#include <QQmlEngine> + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + QQuickView view; + + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (app.arguments().contains(QStringLiteral("--multisample"))) + fmt.setSamples(4); + if (app.arguments().contains(QStringLiteral("--coreprofile"))) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CoreProfile); + } + view.setFormat(fmt); + + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.resize(1024, 768); + view.setSource(QUrl("qrc:/shapestest/shapestest.qml")); + view.show(); + + return app.exec(); +} diff --git a/tests/manual/shapestest/shapestest.pro b/tests/manual/shapestest/shapestest.pro new file mode 100644 index 0000000000..1776175134 --- /dev/null +++ b/tests/manual/shapestest/shapestest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app + +QT += quick qml +SOURCES += main.cpp +RESOURCES += shapestest.qrc +OTHER_FILES += shapestest.qml diff --git a/tests/manual/shapestest/shapestest.qml b/tests/manual/shapestest/shapestest.qml new file mode 100644 index 0000000000..de9e20b8f3 --- /dev/null +++ b/tests/manual/shapestest/shapestest.qml @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Shapes 1.0 + +Rectangle { + id: root + width: 1024 + height: 768 + + property color col: "lightsteelblue" + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.tint(root.col, "#20FFFFFF") } + GradientStop { position: 0.1; color: Qt.tint(root.col, "#20AAAAAA") } + GradientStop { position: 0.9; color: Qt.tint(root.col, "#20666666") } + GradientStop { position: 1.0; color: Qt.tint(root.col, "#20000000") } + } + + Row { + anchors.top: parent.top + anchors.centerIn: parent + spacing: 20 + + Column { + spacing: 20 + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 100 + Shape { + id: triangle + anchors.fill: parent + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: LinearGradient { + x1: 0; y1: 0 + x2: 200; y2: 100 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + fillColor: "blue" // ignored with the gradient set + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + PathLine { x: 200; y: 100 } + PathLine { x: 0; y: 100 } + PathLine { x: 0; y: 0 } + } + transform: Rotation { origin.x: 100; origin.y: 50; axis { x: 0; y: 1; z: 0 } + SequentialAnimation on angle { + NumberAnimation { from: 0; to: 75; duration: 2000 } + NumberAnimation { from: 75; to: -75; duration: 4000 } + NumberAnimation { from: -75; to: 0; duration: 2000 } + loops: Animation.Infinite + } + } + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 100 + Shape { + anchors.fill: parent + ShapePath { + id: someCurve + property color sc: "gray" + strokeColor: sc + property color fc: "yellow" + fillColor: fc + startX: 20; startY: 10 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathLine { x: 150; y: 80 } + PathQuad { x: 180; y: 10; controlX: 200; controlY: 80 } + PathLine { x: 20; y: 10 } + // Dynamic changes via property bindings etc. all work but can + // be computationally expense with the generic backend for properties + // that need retriangulating on every change. Should be cheap with NVPR. + NumberAnimation on strokeWidth { + from: 1; to: 20; duration: 10000 + } + } + } + // Changing colors for a solid stroke or fill is simple and + // (relatively) cheap. However, changing to/from transparent + // stroke/fill color and stroke width 0 are special as these + // change the scenegraph node tree (with the generic backend). + Timer { + interval: 2000 + running: true + repeat: true + onTriggered: someCurve.fillColor = (someCurve.fillColor === someCurve.fc ? "transparent" : someCurve.fc) + } + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: someCurve.strokeColor = (someCurve.strokeColor === someCurve.sc ? "transparent" : someCurve.sc) + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 300 + height: 100 + Shape { + id: linesAndMoves + anchors.fill: parent + ShapePath { + strokeColor: "black" + startX: 0; startY: 50 + PathLine { relativeX: 100; y: 50 } + PathMove { relativeX: 100; y: 50 } + PathLine { relativeX: 100; y: 50 } + } + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 120 + Shape { + anchors.fill: parent + ShapePath { + id: joinTest + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + } + Timer { + interval: 1000 + repeat: true + running: true + property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ] + onTriggered: { + for (var i = 0; i < styles.length; ++i) + if (styles[i] === joinTest.joinStyle) { + joinTest.joinStyle = styles[(i + 1) % styles.length]; + break; + } + } + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 100 + Shape { + anchors.fill: parent + ShapePath { + id: star + strokeColor: "blue" + fillColor: "lightGray" + strokeWidth: 2 + PathMove { x: 90; y: 50 } + PathLine { x: 50 + 40 * Math.cos(0.8 * 1 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 1 * Math.PI) } + PathLine { x: 50 + 40 * Math.cos(0.8 * 2 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 2 * Math.PI) } + PathLine { x: 50 + 40 * Math.cos(0.8 * 3 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 3 * Math.PI) } + PathLine { x: 50 + 40 * Math.cos(0.8 * 4 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 4 * Math.PI) } + PathLine { x: 90; y: 50 } + } + } + Timer { + interval: 1000 + onTriggered: star.fillRule = (star.fillRule === ShapePath.OddEvenFill ? ShapePath.WindingFill : ShapePath.OddEvenFill) + repeat: true + running: true + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 100 + Shape { + anchors.fill: parent + ShapePath { + strokeWidth: 4 + strokeColor: "black" + fillColor: "transparent" + startX: 20; startY: 10 + PathCubic { + id: cb + x: 180; y: 10 + control1X: -10; control1Y: 90; control2Y: 90 + NumberAnimation on control2X { from: 400; to: 0; duration: 5000; loops: Animation.Infinite } + } + } + } + } + } + + Column { + spacing: 20 + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 100 + Shape { + anchors.fill: parent + ShapePath { + fillColor: "transparent" + strokeColor: "red" + strokeWidth: 4 + startX: 10; startY: 40 + PathArc { + x: 10; y: 60 + radiusX: 40; radiusY: 40 + useLargeArc: true + } + } + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 200 + Rectangle { + anchors.centerIn: parent + // have a size smaller than 150x150 + width: 100 + height: 100 + // and enable clipping. Normally this goes via scissoring, unless + // some transform triggers the stencil-based path. Ensure this via rotation. + clip: true + NumberAnimation on rotation { + from: 0; to: 360; duration: 5000; loops: Animation.Infinite + } + + Shape { + width: 150 + height: 150 + + ShapePath { + fillColor: "blue" + strokeColor: "red" + strokeWidth: 4 + startX: 10; startY: 10 + PathLine { x: 140; y: 140 } + PathLine { x: 10; y: 140 } + PathLine { x: 10; y: 10 } + } + } + } + } + + // stencil clip test #2, something more complicated: + Rectangle { + border.color: "purple" + color: "transparent" + width: 150 + height: 150 + Rectangle { + anchors.centerIn: parent + width: 60 + height: 60 + clip: true + NumberAnimation on rotation { + from: 0; to: 360; duration: 5000; loops: Animation.Infinite + } + Shape { + width: 100 + height: 100 + ShapePath { + id: clippedStar + strokeColor: "blue" + fillColor: "lightGray" + strokeWidth: 2 + PathMove { x: 90; y: 50 } + PathLine { x: 50 + 40 * Math.cos(0.8 * 1 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 1 * Math.PI) } + PathLine { x: 50 + 40 * Math.cos(0.8 * 2 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 2 * Math.PI) } + PathLine { x: 50 + 40 * Math.cos(0.8 * 3 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 3 * Math.PI) } + PathLine { x: 50 + 40 * Math.cos(0.8 * 4 * Math.PI); y: 50 + 40 * Math.sin(0.8 * 4 * Math.PI) } + PathLine { x: 90; y: 50 } + } + } + Timer { + interval: 1000 + onTriggered: clippedStar.fillRule = (clippedStar.fillRule === ShapePath.OddEvenFill ? ShapePath.WindingFill : ShapePath.OddEvenFill) + repeat: true + running: true + } + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 100 + height: 100 + Shape { + anchors.fill: parent + ShapePath { + strokeColor: "red" + PathLine { x: 100; y: 100 } + } + ShapePath { + strokeColor: "blue" + startX: 100; startY: 0 + PathLine { x: 0; y: 100 } + } + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 100 + Shape { + anchors.fill: parent + ShapePath { + strokeWidth: -1 + strokeColor: "red" + fillGradient: RadialGradient { + centerX: 100; centerY: 50 + focalX: centerX; focalY: centerY + centerRadius: 50 + spread: RadialGradient.ReflectSpread + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + PathLine { x: 0; y: 100 } + PathLine { x: 200; y: 100 } + PathLine { x: 200; y: 0 } + PathLine { x: 0; y: 0 } + } + } + } + + Rectangle { + border.color: "purple" + color: "transparent" + width: 200 + height: 100 + Shape { + anchors.fill: parent + ShapePath { + strokeWidth: -1 + strokeColor: "red" + fillGradient: ConicalGradient { + centerX: 100; centerY: 50 + angle: 90 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + PathLine { x: 0; y: 100 } + PathLine { x: 200; y: 100 } + PathLine { x: 200; y: 0 } + PathLine { x: 0; y: 0 } + } + } + } + } + } + + Rectangle { + id: stackTestRect + SequentialAnimation on opacity { + NumberAnimation { from: 0; to: 1; duration: 5000 } + PauseAnimation { duration: 2000 } + NumberAnimation { from: 1; to: 0; duration: 5000 } + PauseAnimation { duration: 2000 } + loops: Animation.Infinite + id: opAnim + } + color: "blue" + anchors.margins: 10 + anchors.fill: parent + } + MouseArea { + anchors.fill: parent + onClicked: stackTestRect.visible = !stackTestRect.visible + } +} diff --git a/tests/manual/shapestest/shapestest.qrc b/tests/manual/shapestest/shapestest.qrc new file mode 100644 index 0000000000..6bfc953997 --- /dev/null +++ b/tests/manual/shapestest/shapestest.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/shapestest"> + <file>shapestest.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/touch/mpta-crosshairs.qml b/tests/manual/touch/mpta-crosshairs.qml index d1dbd0f188..efea16029b 100644 --- a/tests/manual/touch/mpta-crosshairs.qml +++ b/tests/manual/touch/mpta-crosshairs.qml @@ -50,6 +50,7 @@ Rectangle { MultiPointTouchArea { id: mpta anchors.fill: parent + //onGestureStarted: gesture.grab() // in case this is embedded in something that might steal touchPoints: [ TouchPoint { property color color: "red" }, TouchPoint { property color color: "orange" }, diff --git a/tests/manual/v4/typedarrays.js b/tests/manual/v4/typedarrays.js index 8cf2b8c75a..f727df7185 100644 --- a/tests/manual/v4/typedarrays.js +++ b/tests/manual/v4/typedarrays.js @@ -670,12 +670,12 @@ function TestDataViewConstructor() { /* This is wrong according to ecma 6 and should throw: - var d4 = new DataView(ab, 1, 3.1415926); + var d4 = new DataView(ab, 1, Math.PI); assertSame(ab, d4.buffer); assertSame(1, d4.byteOffset); assertSame(3, d4.byteLength); */ - assertThrows(function() { new DataView(ab, 3.1415926); }, RangeError); + assertThrows(function() { new DataView(ab, Math.PI); }, RangeError); // error cases assertThrows(function() { new DataView(ab, -1); }, RangeError); |