diff options
76 files changed, 1329 insertions, 212 deletions
diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp index f05bf2f843..5737e419b0 100644 --- a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp +++ b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp @@ -49,6 +49,7 @@ ****************************************************************************/ #include "d3d11squircle.h" +#include <QtCore/QFile> #include <QtCore/QRunnable> #include <QtQuick/QQuickWindow> diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 26c6d4032f..44f0a49d76 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -255,7 +255,8 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path) \list \li \c fileName \li \c filePath - \li \c fileURL (since Qt 5.2) + \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15) + \li \c fileUrl (since Qt 5.15) \li \c fileBaseName \li \c fileSuffix \li \c fileSize @@ -334,7 +335,8 @@ QQuickFolderListModel::QQuickFolderListModel(QObject *parent) d->roleNames[FileLastModifiedRole] = "fileModified"; d->roleNames[FileLastReadRole] = "fileAccessed"; d->roleNames[FileIsDirRole] = "fileIsDir"; - d->roleNames[FileUrlRole] = "fileURL"; + d->roleNames[FileUrlRole] = "fileUrl"; + d->roleNames[FileURLRole] = "fileURL"; d->init(); } @@ -377,6 +379,7 @@ QVariant QQuickFolderListModel::data(const QModelIndex &index, int role) const rv = d->data.at(index.row()).isDir(); break; case FileUrlRole: + case FileURLRole: rv = QUrl::fromLocalFile(d->data.at(index.row()).filePath()); break; default: @@ -886,7 +889,8 @@ void QQuickFolderListModel::setSortCaseSensitive(bool on) \list \li \c fileName \li \c filePath - \li \c fileURL (since Qt 5.2) + \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15) + \li \c fileUrl (since Qt 5.15) \li \c fileBaseName \li \c fileSuffix \li \c fileSize diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.h b/src/imports/folderlistmodel/qquickfolderlistmodel.h index d7429efeda..e21a8d8f37 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.h +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.h @@ -95,7 +95,8 @@ public: FileLastModifiedRole = Qt::UserRole + 6, FileLastReadRole = Qt::UserRole +7, FileIsDirRole = Qt::UserRole + 8, - FileUrlRole = Qt::UserRole + 9 + FileUrlRole = Qt::UserRole + 9, + FileURLRole = Qt::UserRole + 10 }; int rowCount(const QModelIndex &parent = QModelIndex()) const override; diff --git a/src/qml/Qt5QmlConfigExtras.cmake.in b/src/qml/Qt5QmlConfigExtras.cmake.in index 9ddb9885cd..4242143bca 100644 --- a/src/qml/Qt5QmlConfigExtras.cmake.in +++ b/src/qml/Qt5QmlConfigExtras.cmake.in @@ -1,5 +1,7 @@ -file(GLOB _qt5qml_other_plugins "${CMAKE_CURRENT_LIST_DIR}/Qt5Qml_*Factory.cmake") +if(QT5_STRICT_PLUGIN_GLOB OR Qt5Qml_STRICT_PLUGIN_GLOB) + file(GLOB _qt5qml_other_plugins "${CMAKE_CURRENT_LIST_DIR}/Qt5Qml_*Factory.cmake") -foreach(_other_plugin ${_qt5qml_other_plugins}) - include(${_other_plugin} OPTIONAL) -endforeach() + foreach(_other_plugin ${_qt5qml_other_plugins}) + include(${_other_plugin} OPTIONAL) + endforeach() +endif() diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc index 17ac7693bc..c11174db43 100644 --- a/src/qml/doc/src/external-resources.qdoc +++ b/src/qml/doc/src/external-resources.qdoc @@ -76,3 +76,7 @@ \externalpage https://fontawesome.com/ \title Font Awesome */ +/*! + \externalpage https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator + \title Nullish Coalescing +*/ diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index c22c392b80..bc75f843fb 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -42,6 +42,8 @@ Like a browser or server-side JavaScript environment, the QML runtime implements all of the built-in types and functions defined by the standard, such as Object, Array, and Math. The QML runtime implements the 7th edition of the standard. +Since Qt 5.15 \l{Nullish Coalescing} is also implemented in the QML runtime. + The standard ECMAScript built-ins are not explicitly documented in the QML documentation. For more information on their use, please refer to the ECMA-262 7th edition standard or one of the many online JavaScript reference and tutorial sites, such as the \l{W3Schools JavaScript Reference} (JavaScript Objects diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 55884aa42c..51e63f3608 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1562,7 +1562,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int retn = QVariant(typeHint, temp); QMetaType::destroy(typeHint, temp); auto retnAsIterable = retn.value<QtMetaTypePrivate::QSequentialIterableImpl>(); - if (retnAsIterable._iteratorCapabilities & QtMetaTypePrivate::ContainerIsAppendable) { + if (retnAsIterable.containerCapabilities() & QtMetaTypePrivate::ContainerIsAppendable) { auto const length = a->getLength(); QV4::ScopedValue arrayValue(scope); for (qint64 i = 0; i < length; ++i) { diff --git a/src/qml/jsruntime/qv4resolvedtypereference_p.h b/src/qml/jsruntime/qv4resolvedtypereference_p.h index 88b77cf2a8..7e8bedad62 100644 --- a/src/qml/jsruntime/qv4resolvedtypereference_p.h +++ b/src/qml/jsruntime/qv4resolvedtypereference_p.h @@ -67,6 +67,11 @@ class ResolvedTypeReference Q_DISABLE_COPY_MOVE(ResolvedTypeReference) public: ResolvedTypeReference() = default; + ~ResolvedTypeReference() + { + if (m_stronglyReferencesCompilationUnit && m_compilationUnit) + m_compilationUnit->release(); + } QQmlRefPointer<QQmlPropertyCache> propertyCache() const; QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); @@ -80,7 +85,33 @@ public: QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() { return m_compilationUnit; } void setCompilationUnit(QQmlRefPointer<QV4::ExecutableCompilationUnit> unit) { - m_compilationUnit = std::move(unit); + if (m_compilationUnit == unit.data()) + return; + if (m_stronglyReferencesCompilationUnit) { + if (m_compilationUnit) + m_compilationUnit->release(); + m_compilationUnit = unit.take(); + } else { + m_compilationUnit = unit.data(); + } + } + + bool referencesCompilationUnit() const { return m_stronglyReferencesCompilationUnit; } + void setReferencesCompilationUnit(bool doReference) + { + if (doReference == m_stronglyReferencesCompilationUnit) + return; + m_stronglyReferencesCompilationUnit = doReference; + if (!m_compilationUnit) + return; + if (doReference) { + m_compilationUnit->addref(); + } else if (m_compilationUnit->count() == 1) { + m_compilationUnit->release(); + m_compilationUnit = nullptr; + } else { + m_compilationUnit->release(); + } } QQmlRefPointer<QQmlPropertyCache> typePropertyCache() const { return m_typePropertyCache; } @@ -98,12 +129,13 @@ public: private: QQmlType m_type; QQmlRefPointer<QQmlPropertyCache> m_typePropertyCache; - QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit; + QV4::ExecutableCompilationUnit *m_compilationUnit = nullptr; QTypeRevision m_version = QTypeRevision::zero(); // Types such as QQmlPropertyMap can add properties dynamically at run-time and // therefore cannot have a property cache installed when instantiated. bool m_isFullyDynamicType = false; + bool m_stronglyReferencesCompilationUnit = true; }; } // namespace QV4 diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 6fe8dc5227..a2976b59c3 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -107,8 +107,6 @@ void QQmlApplicationEnginePrivate::_q_loadTranslations() activeTranslator.reset(); } q->retranslate(); -#else - Q_UNUSED(rootFile) #endif } diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 70232b3d52..4568f7cfbb 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -76,8 +76,10 @@ public: QVariantMap initialProperties; QStringList extraFileSelectors; QString translationsDirectory; +#if QT_CONFIG(translation) QScopedPointer<QTranslator> activeTranslator; bool isInitialized = false; +#endif }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index b264528daa..fea0062242 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -300,7 +300,14 @@ void QQmlTypeData::setCompileUnit(const Container &container) auto const root = container->objectAt(i); for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) { auto *typeRef = m_compiledData->resolvedType(it->nameIndex); - typeRef->setCompilationUnit(m_compiledData); // share compilation unit + + // We don't want the type reference to keep a strong reference to the compilation unit + // here. The compilation unit owns the type reference, and having a strong reference + // would prevent the compilation unit from ever getting deleted. We can still be sure + // that the compilation unit outlives the type reference, due to ownership. + typeRef->setReferencesCompilationUnit(false); + + typeRef->setCompilationUnit(m_compiledData); // share compilation unit } } } diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp index 9d495ce6e4..81b69d1216 100644 --- a/src/qmldebug/qqmldebugconnection.cpp +++ b/src/qmldebug/qqmldebugconnection.cpp @@ -416,8 +416,7 @@ public: { connect(parent, &QLocalSocket::stateChanged, this, &LocalSocketSignalTranslator::onStateChanged); - connect(parent, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>( - &QLocalSocket::errorOccurred), this, &LocalSocketSignalTranslator::onError); + connect(parent, &QLocalSocket::errorOccurred, this, &LocalSocketSignalTranslator::onError); } void onError(QLocalSocket::LocalSocketError error) diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 646ac5e9f9..c94493dee8 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -926,17 +926,11 @@ void PropertyUpdater::doUpdate() auto mo = sender->metaObject(); auto signalIndex = QObject::senderSignalIndex(); ++updateCount; - // start at 0 instead of propertyOffset to handle properties from parent hierarchy - for (auto i = 0; i < mo->propertyCount() + mo->propertyOffset(); ++i) { - auto property = mo->property(i); - if (property.notifySignal().methodIndex() == signalIndex) { - // we synchronize between required properties and model rolenames by name - // that's why the QQmlProperty and the metaobject property must have the same name - QQmlProperty qmlProp(parent(), QString::fromLatin1(property.name())); - qmlProp.write(property.read(QObject::sender())); - return; - } - } + auto property = mo->property(changeSignalIndexToPropertyIndex[signalIndex]); + // we synchronize between required properties and model rolenames by name + // that's why the QQmlProperty and the metaobject property must have the same name + QQmlProperty qmlProp(parent(), QString::fromLatin1(property.name())); + qmlProp.write(property.read(QObject::sender())); } void PropertyUpdater::breakBinding() @@ -1014,8 +1008,10 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod QMetaMethod changeSignal = prop.notifySignal(); static QMetaMethod updateSlot = PropertyUpdater::staticMetaObject.method( PropertyUpdater::staticMetaObject.indexOfSlot("doUpdate()")); + QMetaObject::Connection conn = QObject::connect(itemOrProxy, changeSignal, updater, updateSlot); + updater->changeSignalIndexToPropertyIndex[changeSignal.methodIndex()] = i; auto propIdx = object->metaObject()->indexOfProperty(propName.toUtf8()); QMetaMethod writeToPropSignal = object->metaObject()->property(propIdx).notifySignal(); diff --git a/src/qmlmodels/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h index 20fb47f021..491e8025b8 100644 --- a/src/qmlmodels/qqmldelegatemodel_p_p.h +++ b/src/qmlmodels/qqmldelegatemodel_p_p.h @@ -475,6 +475,7 @@ class PropertyUpdater : public QObject public: PropertyUpdater(QObject *parent); QHash<int, QMetaObject::Connection> senderToConnection; + QHash<int, int> changeSignalIndexToPropertyIndex; int updateCount = 0; public Q_SLOTS: void doUpdate(); diff --git a/src/qmlmodels/qqmltableinstancemodel.cpp b/src/qmlmodels/qqmltableinstancemodel.cpp index 4152cbe3ea..332a5447b1 100644 --- a/src/qmlmodels/qqmltableinstancemodel.cpp +++ b/src/qmlmodels/qqmltableinstancemodel.cpp @@ -232,14 +232,17 @@ QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, } // The item is not reused or referenced by anyone, so just delete it - destroyModelItem(modelItem); + destroyModelItem(modelItem, Deferred); return QQmlInstanceModel::Destroyed; } -void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem) +void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode) { emit destroyingItem(modelItem->object); - modelItem->destroyObject(); + if (mode == Deferred) + modelItem->destroyObject(); + else + delete modelItem->object; delete modelItem; } @@ -284,7 +287,9 @@ void QQmlTableInstanceModel::cancel(int index) void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime) { - m_reusableItemsPool.drain(maxPoolTime, [=](QQmlDelegateModelItem *modelItem){ destroyModelItem(modelItem); }); + m_reusableItemsPool.drain(maxPoolTime, [this](QQmlDelegateModelItem *modelItem) { + destroyModelItem(modelItem, Immediate); + }); } void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex) diff --git a/src/qmlmodels/qqmltableinstancemodel_p.h b/src/qmlmodels/qqmltableinstancemodel_p.h index 57b9b26e43..defe513ef9 100644 --- a/src/qmlmodels/qqmltableinstancemodel_p.h +++ b/src/qmlmodels/qqmltableinstancemodel_p.h @@ -124,6 +124,11 @@ public: int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; } private: + enum DestructionMode { + Deferred, + Immediate + }; + QQmlComponent *resolveDelegate(int index); QQmlAdaptorModel m_adaptorModel; @@ -141,7 +146,7 @@ private: void deleteIncubationTaskLater(QQmlIncubator *incubationTask); void deleteAllFinishedIncubationTasks(); QQmlDelegateModelItem *resolveModelItem(int index); - void destroyModelItem(QQmlDelegateModelItem *modelItem); + void destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode); void dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles); diff --git a/src/qmltest/qmldir b/src/qmltest/qmldir new file mode 100644 index 0000000000..5e9d5e2c95 --- /dev/null +++ b/src/qmltest/qmldir @@ -0,0 +1,2 @@ +module Qt.test.qtestroot +typeinfo plugins.qmltypes diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index 6864203ba4..c2e8068fc6 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -22,6 +22,7 @@ SOURCES += \ HEADERS += \ $$PWD/quicktestglobal.h \ $$PWD/quicktest.h \ + $$PWD/quicktest_p.h \ $$PWD/quicktestresult_p.h \ $$PWD/qtestoptions_p.h @@ -29,4 +30,15 @@ qtConfig(qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING load(qt_module) -CONFIG += metatypes install_metatypes +QMLTYPES_FILENAME = plugins.qmltypes +QMLTYPES_INSTALL_DIR = $$[QT_INSTALL_QML]/Qt/test/qtestroot +QML_IMPORT_NAME = Qt.test.qtestroot +QML_IMPORT_VERSION = 1.0 +CONFIG += qmltypes install_qmltypes install_metatypes + +# Install qmldir +qmldir.files = $$PWD/qmldir +qmldir.path = $$QMLTYPES_INSTALL_DIR + +prefix_build: INSTALLS += qmldir +else: COPIES += qmldir diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index b2f54d978f..63e51fa4ba 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -37,7 +37,7 @@ ** ****************************************************************************/ -#include "quicktest.h" +#include "quicktest_p.h" #include "quicktestresult_p.h" #include <QtTest/qtestsystem.h> #include "qtestoptions_p.h" @@ -133,53 +133,6 @@ bool QQuickTest::qWaitForItemPolished(const QQuickItem *item, int timeout) return QTest::qWaitFor([&]() { return !QQuickItemPrivate::get(item)->polishScheduled; }, timeout); } -class QTestRootObject : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged) - Q_PROPERTY(bool hasTestCase READ hasTestCase WRITE setHasTestCase NOTIFY hasTestCaseChanged) - Q_PROPERTY(QObject *defined READ defined) -public: - QTestRootObject(QObject *parent = nullptr) - : QObject(parent), hasQuit(false), m_windowShown(false), m_hasTestCase(false) { - m_defined = new QQmlPropertyMap(this); -#if defined(QT_OPENGL_ES_2_ANGLE) - m_defined->insert(QLatin1String("QT_OPENGL_ES_2_ANGLE"), QVariant(true)); -#endif - } - - static QTestRootObject *instance() { - static QPointer<QTestRootObject> object = new QTestRootObject; - if (!object) { - // QTestRootObject was deleted when previous test ended, create a new one - object = new QTestRootObject; - } - return object; - } - - bool hasQuit:1; - bool hasTestCase() const { return m_hasTestCase; } - void setHasTestCase(bool value) { m_hasTestCase = value; emit hasTestCaseChanged(); } - - bool windowShown() const { return m_windowShown; } - void setWindowShown(bool value) { m_windowShown = value; emit windowShownChanged(); } - QQmlPropertyMap *defined() const { return m_defined; } - - void init() { setWindowShown(false); setHasTestCase(false); hasQuit = false; } - -Q_SIGNALS: - void windowShownChanged(); - void hasTestCaseChanged(); - -private Q_SLOTS: - void quit() { hasQuit = true; } - -private: - bool m_windowShown : 1; - bool m_hasTestCase :1; - QQmlPropertyMap *m_defined; -}; - static QObject *testRootObject(QQmlEngine *engine, QJSEngine *jsEngine) { Q_UNUSED(engine); @@ -556,7 +509,7 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch qputenv("QT_QTESTLIB_RUNNING", "1"); - // Register the test object + // Register the custom factory function qmlRegisterSingletonType<QTestRootObject>("Qt.test.qtestroot", 1, 0, "QTestRootObject", testRootObject); QSet<QString> commandLineTestFunctions(QTest::testFunctions.cbegin(), QTest::testFunctions.cend()); @@ -688,5 +641,3 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch } QT_END_NAMESPACE - -#include "quicktest.moc" diff --git a/src/qmltest/quicktest_p.h b/src/qmltest/quicktest_p.h new file mode 100644 index 0000000000..50fc3be050 --- /dev/null +++ b/src/qmltest/quicktest_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUICKTEST_P_H +#define QUICKTEST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTest/quicktest.h> + +#include <QtQml/qqmlpropertymap.h> +#include <QtQml/qqml.h> + +QT_BEGIN_NAMESPACE + +class QTestRootObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged) + Q_PROPERTY(bool hasTestCase READ hasTestCase WRITE setHasTestCase NOTIFY hasTestCaseChanged) + Q_PROPERTY(QObject *defined READ defined) + QML_SINGLETON + QML_ELEMENT + +public: + QTestRootObject(QObject *parent = nullptr) + : QObject(parent), hasQuit(false), m_windowShown(false), m_hasTestCase(false) { + m_defined = new QQmlPropertyMap(this); +#if defined(QT_OPENGL_ES_2_ANGLE) + m_defined->insert(QLatin1String("QT_OPENGL_ES_2_ANGLE"), QVariant(true)); +#endif + } + + static QTestRootObject *instance() { + static QPointer<QTestRootObject> object = new QTestRootObject; + if (!object) { + // QTestRootObject was deleted when previous test ended, create a new one + object = new QTestRootObject; + } + return object; + } + + bool hasQuit:1; + bool hasTestCase() const { return m_hasTestCase; } + void setHasTestCase(bool value) { m_hasTestCase = value; emit hasTestCaseChanged(); } + + bool windowShown() const { return m_windowShown; } + void setWindowShown(bool value) { m_windowShown = value; emit windowShownChanged(); } + QQmlPropertyMap *defined() const { return m_defined; } + + void init() { setWindowShown(false); setHasTestCase(false); hasQuit = false; } + +Q_SIGNALS: + void windowShownChanged(); + void hasTestCaseChanged(); + +private Q_SLOTS: + void quit() { hasQuit = true; } + +private: + bool m_windowShown : 1; + bool m_hasTestCase :1; + QQmlPropertyMap *m_defined; +}; + +QT_END_NAMESPACE + +#endif // QUICKTEST_P_H diff --git a/src/qmltyperegistrar/qmltyperegistrar.cpp b/src/qmltyperegistrar/qmltyperegistrar.cpp index 33f1ebbbd5..e6b0ee8cd9 100644 --- a/src/qmltyperegistrar/qmltyperegistrar.cpp +++ b/src/qmltyperegistrar/qmltyperegistrar.cpp @@ -331,25 +331,37 @@ int main(int argc, char **argv) } } + const bool privateIncludes = parser.isSet(privateIncludesOption); + auto resolvedInclude = [&](const QString &include) { + return (privateIncludes && include.endsWith(QLatin1String("_p.h"))) + ? QLatin1String("private/") + include + : include; + }; + auto processMetaObject = [&](const QJsonObject &metaObject) { + const QString include = resolvedInclude(metaObject[QLatin1String("inputFile")].toString()); const QJsonArray classes = metaObject[QLatin1String("classes")].toArray(); for (const auto &cls : classes) { QJsonObject classDef = cls.toObject(); + classDef.insert(QLatin1String("inputFile"), include); + switch (qmlTypeRegistrationMode(classDef)) { case NamespaceRegistration: case GadgetRegistration: case ObjectRegistration: { - const QString include = metaObject[QLatin1String("inputFile")].toString(); - const bool declaredInHeader = include.endsWith(QLatin1String(".h")); - if (declaredInHeader) { - includes.append(include); - classDef.insert(QLatin1String("registerable"), true); - } else { - fprintf(stderr, "Cannot generate QML type registration for class %s " - "because it is not declared in a header.", + if (!include.endsWith(QLatin1String(".h")) + && !include.endsWith(QLatin1String(".hpp")) + && !include.endsWith(QLatin1String(".hxx")) + && include.contains(QLatin1Char('.'))) { + fprintf(stderr, + "Class %s is declared in %s, which appears not to be a header.\n" + "The compilation of its registration to QML may fail.\n", qPrintable(classDef.value(QLatin1String("qualifiedClassName")) - .toString())); + .toString()), + qPrintable(include)); } + includes.append(include); + classDef.insert(QLatin1String("registerable"), true); types.append(classDef); break; @@ -395,13 +407,8 @@ int main(int argc, char **argv) const auto newEnd = std::unique(includes.begin(), includes.end()); includes.erase(newEnd, includes.end()); - const bool privateIncludes = parser.isSet(privateIncludesOption); - for (const QString &include : qAsConst(includes)) { - if (privateIncludes && include.endsWith(QLatin1String("_p.h"))) - fprintf(output, "\n#include <private/%s>", qPrintable(include)); - else - fprintf(output, "\n#include <%s>", qPrintable(include)); - } + for (const QString &include : qAsConst(includes)) + fprintf(output, "\n#include <%s>", qPrintable(include)); fprintf(output, "\n\n"); @@ -463,9 +470,13 @@ int main(int argc, char **argv) continue; } + const QString include = metaObject[QLatin1String("inputFile")].toString(); const QJsonArray classes = metaObject[QLatin1String("classes")].toArray(); - for (const auto &cls : classes) - foreignTypes.append(cls.toObject()); + for (const auto &cls : classes) { + QJsonObject classDef = cls.toObject(); + classDef.insert(QLatin1String("inputFile"), include); + foreignTypes.append(classDef); + } } } } diff --git a/src/qmltyperegistrar/qmltypes.prf b/src/qmltyperegistrar/qmltypes.prf index fbb00dbe2d..0d5a6ded24 100644 --- a/src/qmltyperegistrar/qmltypes.prf +++ b/src/qmltyperegistrar/qmltypes.prf @@ -31,7 +31,7 @@ isEmpty(QML_IMPORT_MINOR_VERSION) { isEmpty(QMLTYPES_FILENAME) { plugin: QMLTYPES_FILENAME = plugins.qmltypes - else: QMLTYPES_FILENAME = $${TEMPLATE}.qmltypes + else: QMLTYPES_FILENAME = $${TARGET}.qmltypes } qt_module_deps = $$replace(QT, -private$, '') @@ -40,25 +40,25 @@ qt_module_deps = $$resolve_depends(qt_module_deps, "QT.", ".depends" ".run_depen qt_module_deps = $$replace(qt_module_deps, _private$, '') qt_module_deps = $$unique(qt_module_deps) -foreign_types = for(dep, qt_module_deps) { METATYPES_FILENAME = $$lower($$eval(QT.$${dep}.module))_metatypes.json INSTALLED_METATYPES = $$[QT_INSTALL_LIBS]/metatypes/$$METATYPES_FILENAME isEmpty(MODULE_BASE_OUTDIR) { - foreign_types += $$INSTALLED_METATYPES + QML_FOREIGN_METATYPES += $$INSTALLED_METATYPES } else { MODULE_BASE_METATYPES = $$MODULE_BASE_OUTDIR/lib/metatypes/$$METATYPES_FILENAME - exists($$MODULE_BASE_METATYPES): foreign_types += $$MODULE_BASE_METATYPES - else: foreign_types += $$INSTALLED_METATYPES + exists($$MODULE_BASE_METATYPES): QML_FOREIGN_METATYPES += $$MODULE_BASE_METATYPES + else: QML_FOREIGN_METATYPES += $$INSTALLED_METATYPES } } + QML_TYPEREGISTRAR_FLAGS = \ --generate-qmltypes=$$QMLTYPES_FILENAME \ --import-name=$$QML_IMPORT_NAME \ --major-version=$$QML_IMPORT_MAJOR_VERSION \ --minor-version=$$QML_IMPORT_MINOR_VERSION \ - --foreign-types=$$join(foreign_types, ',') + --foreign-types=$$join(QML_FOREIGN_METATYPES, ',') DEPENDENCIESFILE = $$_PRO_FILE_PWD_/dependencies.json exists($$DEPENDENCIESFILE): QML_TYPEREGISTRAR_FLAGS += --dependencies=$$DEPENDENCIESFILE diff --git a/src/qmltyperegistrar/qmltypesclassdescription.cpp b/src/qmltyperegistrar/qmltypesclassdescription.cpp index e21abd97bc..778d057240 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.cpp +++ b/src/qmltyperegistrar/qmltypesclassdescription.cpp @@ -63,6 +63,8 @@ void QmlTypesClassDescription::collect(const QJsonObject *classDef, const QVector<QJsonObject> &foreign, CollectMode mode, QTypeRevision defaultRevision) { + if (file.isEmpty() && classDef->value(QLatin1String("registerable")).toBool()) + file = classDef->value(QLatin1String("inputFile")).toString(); const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray(); for (const QJsonValue &classInfo : classInfos) { const QJsonObject obj = classInfo.toObject(); diff --git a/src/qmltyperegistrar/qmltypesclassdescription.h b/src/qmltyperegistrar/qmltypesclassdescription.h index e4ae37a84e..abe68d42ed 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.h +++ b/src/qmltyperegistrar/qmltypesclassdescription.h @@ -38,6 +38,7 @@ struct QmlTypesClassDescription { const QJsonObject *resolvedClass = nullptr; + QString file; QString elementName; QString defaultProp; QString superClass; diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp index 3569bbe253..d1378d809d 100644 --- a/src/qmltyperegistrar/qmltypescreator.cpp +++ b/src/qmltyperegistrar/qmltypescreator.cpp @@ -46,6 +46,8 @@ static QString enquote(const QString &string) void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &collector) { + if (!collector.file.isEmpty()) + m_qml.writeScriptBinding(QLatin1String("file"), enquote(collector.file)); m_qml.writeScriptBinding( QLatin1String("name"), enquote(collector.resolvedClass->value( diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 8361be7277..12a4ce32df 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -986,10 +986,11 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE pixelData->setPrototypeOf(p); if (image.isNull()) { - *pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32); + *pixelData->d()->image = QImage(qRound(w), qRound(h), QImage::Format_ARGB32); pixelData->d()->image->fill(0x00000000); } else { - Q_ASSERT(image.width()== qRound(w * image.devicePixelRatioF()) && image.height() == qRound(h * image.devicePixelRatioF())); + // After qtbase 88e56d0932a3615231adf40d5ae033e742d72c33, the image size can be off by one. + Q_ASSERT(qAbs(image.width() - qRound(w * image.devicePixelRatioF())) <= 1 && qAbs(image.height() - qRound(h * image.devicePixelRatioF())) <= 1); *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32); } diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 9469a9a06c..22b8877b9f 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -2171,7 +2171,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i tpCopy.setPos(item->mapFromScene(tpCopy.scenePos())); tpCopy.setLastPos(item->mapFromScene(tpCopy.lastScenePos())); tpCopy.setStartPos(item->mapFromScene(tpCopy.startScenePos())); - tpCopy.setRect(item->mapRectFromScene(tpCopy.sceneRect())); + tpCopy.setEllipseDiameters(tpCopy.ellipseDiameters()); tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); touchPoints << tpCopy; } diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index c7f641cf1f..c91a5ef92b 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1897,6 +1897,9 @@ void QQuickFlickable::cancelFlick() void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o) { + if (!prop || !prop->data) + return; + if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) { i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem); } else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) { diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index a1f8fd35d8..cb1d3e224e 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3977,6 +3977,9 @@ void QQuickItem::focusOutEvent(QFocusEvent * /*event*/) press events for an item. The event information is provided by the \a event parameter. + In order to receive mouse press events, \l acceptedMouseButtons() must + return the relevant mouse button. + \input item.qdocinc accepting-events */ void QQuickItem::mousePressEvent(QMouseEvent *event) @@ -3989,6 +3992,10 @@ void QQuickItem::mousePressEvent(QMouseEvent *event) move events for an item. The event information is provided by the \a event parameter. + In order to receive mouse movement events, the preceding mouse press event + must be accepted (by overriding \l mousePressEvent(), for example) and + \l acceptedMouseButtons() must return the relevant mouse button. + \input item.qdocinc accepting-events */ void QQuickItem::mouseMoveEvent(QMouseEvent *event) @@ -4001,6 +4008,10 @@ void QQuickItem::mouseMoveEvent(QMouseEvent *event) release events for an item. The event information is provided by the \a event parameter. + In order to receive mouse release events, the preceding mouse press event + must be accepted (by overriding \l mousePressEvent(), for example) and + \l acceptedMouseButtons() must return the relevant mouse button. + \input item.qdocinc accepting-events */ void QQuickItem::mouseReleaseEvent(QMouseEvent *event) diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index fd2458494c..f54858e4eb 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -1063,6 +1063,12 @@ void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value) } setHovered(!d->hovered); } + if (d->pressed && (!isVisible())) { + // This happens when the mouse area sets itself disabled or hidden + // inside the press handler. In that case we should not keep the internal + // state as pressed, since we never became the mouse grabber. + ungrabMouse(); + } break; default: break; diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index a854d97f7e..383718c979 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -789,7 +789,9 @@ void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QT dtp->setPressure(p->pressure()); dtp->setRotation(p->rotation()); dtp->setVelocity(p->velocity()); - dtp->setArea(p->rect()); + QRectF area(QPointF(), p->ellipseDiameters()); + area.moveCenter(p->pos()); + dtp->setArea(area); dtp->setStartX(p->startPos().x()); dtp->setStartY(p->startPos().y()); dtp->setPreviousX(p->lastPos().x()); diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index f963bdf74f..0692a1da42 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -194,7 +194,7 @@ QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate() */ /*! - \qmlsignal QtQuick::PinchArea::pinchStarted() + \qmlsignal QtQuick::PinchArea::pinchStarted(PinchEvent pinch) This signal is emitted when the pinch area detects that a pinch gesture has started: two touch points (fingers) have been detected, and they have moved @@ -213,7 +213,7 @@ QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate() */ /*! - \qmlsignal QtQuick::PinchArea::pinchUpdated() + \qmlsignal QtQuick::PinchArea::pinchUpdated(PinchEvent pinch) This signal is emitted when the pinch area detects that a pinch gesture has changed. @@ -225,7 +225,7 @@ QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate() */ /*! - \qmlsignal QtQuick::PinchArea::pinchFinished() + \qmlsignal QtQuick::PinchArea::pinchFinished(PinchEvent pinch) This signal is emitted when the pinch area detects that a pinch gesture has finished. @@ -235,7 +235,7 @@ QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate() */ /*! - \qmlsignal QtQuick::PinchArea::smartZoom() + \qmlsignal QtQuick::PinchArea::smartZoom(PinchEvent pinch) \since 5.5 This signal is emitted when the pinch area detects the smart zoom gesture. diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h index dc1c820309..eade79e683 100644 --- a/src/quick/items/qquickscreen_p.h +++ b/src/quick/items/qquickscreen_p.h @@ -78,9 +78,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickScreenInfo : public QObject Q_PROPERTY(qreal logicalPixelDensity READ logicalPixelDensity NOTIFY logicalPixelDensityChanged) Q_PROPERTY(qreal pixelDensity READ pixelDensity NOTIFY pixelDensityChanged) Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged) - // TODO Qt 6 Rename primaryOrientation to orientation Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ primaryOrientation NOTIFY primaryOrientationChanged) - // TODO Qt 6 Remove this orientation -> incomplete device orientation -> better use OrientationSensor Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged) Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION(2, 3)) diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 3548d20706..5adfd20bf0 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -1,4 +1,4 @@ -/**************************************************************************** +/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ @@ -193,8 +193,10 @@ \qmlproperty int QtQuick::TableView::rows \readonly - This property holds the number of rows in the table. This is - equal to the number of rows in the model. + This property holds the number of rows in the table. + + \note \a rows is usually equal to the number of rows in the model, but can + temporarily differ until all pending model changes have been processed. This property is read only. */ @@ -203,9 +205,12 @@ \qmlproperty int QtQuick::TableView::columns \readonly - This property holds the number of columns in the table. This is - equal to the number of columns in the model. If the model is - a list, columns will be \c 1. + This property holds the number of rows in the table. + + \note \a columns is usually equal to the number of columns in the model, but + can temporarily differ until all pending model changes have been processed. + + If the model is a list, columns will be \c 1. This property is read only. */ @@ -549,6 +554,8 @@ QQuickTableViewPrivate::~QQuickTableViewPrivate() QString QQuickTableViewPrivate::tableLayoutToString() const { + if (loadedItems.isEmpty()) + return QLatin1String("table is empty!"); return QString(QLatin1String("table cells: (%1,%2) -> (%3,%4), item count: %5, table rect: %6,%7 x %8,%9")) .arg(leftColumn()).arg(topRow()) .arg(rightColumn()).arg(bottomRow()) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index acf6fd68c8..4874af44ed 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -976,8 +976,7 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); for (int i = 0; i < touchPoints.count(); ++i) { QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; - - touchPoint.setSceneRect(touchPoint.rect()); + touchPoint.setScenePos(touchPoint.pos()); touchPoint.setStartScenePos(touchPoint.startPos()); touchPoint.setLastScenePos(touchPoint.lastPos()); } diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 14314b2c94..ed2f8c313f 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -242,8 +242,6 @@ void QSGDefaultRenderContext::beginNextRhiFrame(QSGRenderer *renderer, QRhiRende RenderPassCallback mainPassRecordingEnd, void *callbackUserData) { - Q_ASSERT(!m_currentFrameCommandBuffer); - renderer->setRenderTarget(rt); renderer->setRenderPassDescriptor(rp); renderer->setCommandBuffer(cb); diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp index 90c725a67f..e5e25d141b 100644 --- a/src/quick/util/qquickanimation.cpp +++ b/src/quick/util/qquickanimation.cpp @@ -176,11 +176,8 @@ void QQuickAbstractAnimationPrivate::commence() animationInstance = new QQuickAnimatorProxyJob(animationInstance, q); animationInstance->addAnimationChangeListener(this, QAbstractAnimationJob::Completion); } + emit q->started(); animationInstance->start(); - if (animationInstance->isStopped()) { - running = false; - emit q->stopped(); - } } } @@ -287,10 +284,8 @@ void QQuickAbstractAnimation::setRunning(bool r) d->animationInstance->setLoopCount(d->animationInstance->currentLoop() + d->loopCount); supressStart = true; //we want the animation to continue, rather than restart } - if (!supressStart) { + if (!supressStart) d->commence(); - emit started(); - } } else { if (d->paused) { d->paused = false; //reset paused state to false when stopped @@ -308,7 +303,16 @@ void QQuickAbstractAnimation::setRunning(bool r) } } - emit runningChanged(d->running); + + if (r == d->running) { + // This might happen if we start an animation with 0 duration: This will result in that + // commence() will emit started(), and then when it starts it will call setCurrentTime(0), + // (which is both start and end time of the animation), so it will also end up calling + // setRunning(false) (recursively) and stop the animation. + // Therefore, the state of d->running will in that case be different than r if we are back in + // the root stack frame of the recursive calls to setRunning() + emit runningChanged(d->running); + } } /*! diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index db7ae22767..59d4a89fbf 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -109,6 +109,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) renderControl = new QQuickWidgetRenderControl(q); offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl); offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); + offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); // Do not call create() on offscreenWindow. // Check if the Software Adaptation is being used @@ -804,15 +805,29 @@ void QQuickWidgetPrivate::updateSize() q->updateGeometry(); } } else if (resizeMode == QQuickWidget::SizeRootObjectToView) { - bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width()); - bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height()); - - if (needToUpdateWidth && needToUpdateHeight) - root->setSize(QSizeF(q->width(), q->height())); - else if (needToUpdateWidth) - root->setWidth(q->width()); - else if (needToUpdateHeight) - root->setHeight(q->height()); + const bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width()); + const bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height()); + + if (needToUpdateWidth && needToUpdateHeight) { + // Make sure that we have realistic sizing behavior by following + // what on-screen windows would do and resize everything, not just + // the root item. We do this because other types may be relying on + // us to behave correctly. + const QSizeF newSize(q->width(), q->height()); + offscreenWindow->resize(newSize.toSize()); + offscreenWindow->contentItem()->setSize(newSize); + root->setSize(newSize); + } else if (needToUpdateWidth) { + const int newWidth = q->width(); + offscreenWindow->setWidth(newWidth); + offscreenWindow->contentItem()->setWidth(newWidth); + root->setWidth(newWidth); + } else if (needToUpdateHeight) { + const int newHeight = q->height(); + offscreenWindow->setHeight(newHeight); + offscreenWindow->contentItem()->setHeight(newHeight); + root->setHeight(newHeight); + } } } diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 418d3d4cb5..bc90d6df03 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -104,3 +104,6 @@ qtConfig(private_tests): \ qtNomakeTools( \ qmlplugindump \ ) + +!cross_compile: \ + SUBDIRS += qmltyperegistrar diff --git a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml new file mode 100644 index 0000000000..620fbf4120 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml @@ -0,0 +1,3 @@ +// This comment should be directly above Item after formatting +Item { +} diff --git a/tests/auto/qml/qmlformat/data/FrontInline.qml b/tests/auto/qml/qmlformat/data/FrontInline.qml new file mode 100644 index 0000000000..c63265481c --- /dev/null +++ b/tests/auto/qml/qmlformat/data/FrontInline.qml @@ -0,0 +1,2 @@ +Item { // This comment should be directly above Item after formatting +} diff --git a/tests/auto/qml/qmlformat/data/IfBlocks.formatted.qml b/tests/auto/qml/qmlformat/data/IfBlocks.formatted.qml new file mode 100644 index 0000000000..b8e77ec23a --- /dev/null +++ b/tests/auto/qml/qmlformat/data/IfBlocks.formatted.qml @@ -0,0 +1,63 @@ +Item { + + function test() { + //// The following if blocks should NOT HAVE braces + // Single branch, no braces + if (true) + console.log("foo"); + + // Single branch, no braces + if (true) + console.log("foo"); + + // Multiple branches, No braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else + console.log("baz"); + // Multiple branches, all braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else + console.log("baz"); + + //// The following if blocks should HAVE braces + // Single branch, braces + if (true) { + console.log("foo"); + console.log("bar"); + } + // Multiple branches, some braces + if (true) { + console.log("foo"); + console.log("foo2"); + } else if (false) { + console.log("bar"); + } else { + console.log("baz"); + } + // Multiple branches, some braces + if (true) { + console.log("foo"); + } else if (false) { + console.log("bar"); + console.log("bar2"); + } else { + console.log("baz"); + } + // Multiple branches, some braces + if (true) { + console.log("foo"); + } else if (false) { + console.log("bar"); + } else { + console.log("baz"); + console.log("baz2"); + } + } + +} diff --git a/tests/auto/qml/qmlformat/data/IfBlocks.qml b/tests/auto/qml/qmlformat/data/IfBlocks.qml new file mode 100644 index 0000000000..505988b238 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/IfBlocks.qml @@ -0,0 +1,66 @@ +Item { + function test() { + //// The following if blocks should NOT HAVE braces + // Single branch, no braces + if (true) + console.log("foo"); + + // Single branch, no braces + if (true) { + console.log("foo"); + } + + + // Multiple branches, No braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else + console.log("baz"); + + // Multiple branches, all braces + if (true) { + console.log("foo"); + } else if (false) { + console.log("bar"); + } else { + console.log("baz"); + } + + //// The following if blocks should HAVE braces + // Single branch, braces + if (true) { + console.log("foo"); + console.log("bar"); + } + + // Multiple branches, some braces + if (true) { + console.log("foo"); + console.log("foo2"); + } else if (false) + console.log("bar"); + else + console.log("baz"); + + // Multiple branches, some braces + if (true) + console.log("foo"); + else if (false) { + console.log("bar"); + console.log("bar2"); + } else + console.log("baz"); + + // Multiple branches, some braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else { + console.log("baz"); + console.log("baz2"); + } + } +} diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp index 21d5ae46a9..997a5cc753 100644 --- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp +++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp @@ -27,8 +27,11 @@ ****************************************************************************/ #include <QtTest/QtTest> +#include <QDir> +#include <QFile> #include <QProcess> #include <QString> +#include <QTemporaryDir> #include <util.h> @@ -43,6 +46,9 @@ private Q_SLOTS: void testFormatNoSort(); void testAnnotations(); void testAnnotationsNoSort(); + void testLineEndings(); + void testFrontInline(); + void testIfBlocks(); void testReadOnlyProps(); void testStatesAndTransitions(); @@ -55,7 +61,7 @@ private Q_SLOTS: private: QString readTestFile(const QString &path); - QString runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed); + QString runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed, const QString &newlineFormat = "native"); QString m_qmlformatPath; QStringList m_excludedDirs; @@ -113,6 +119,7 @@ void TestQmlformat::initTestCase() m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml"; m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml"; m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml"; m_invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml"; m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml"; m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml"; @@ -197,6 +204,16 @@ void TestQmlformat::testAnnotationsNoSort() QCOMPARE(runQmlformat(testFile("Annotations.qml"), false, true), readTestFile("Annotations.formatted.nosort.qml")); } +void TestQmlformat::testFrontInline() +{ + QCOMPARE(runQmlformat(testFile("FrontInline.qml"), false, true), readTestFile("FrontInline.formatted.qml")); +} + +void TestQmlformat::testIfBlocks() +{ + QCOMPARE(runQmlformat(testFile("IfBlocks.qml"), false, true), readTestFile("IfBlocks.formatted.qml")); +} + void TestQmlformat::testReadOnlyProps() { QCOMPARE(runQmlformat(testFile("readOnlyProps.qml"), false, true), readTestFile("readOnlyProps.formatted.qml")); @@ -212,6 +229,23 @@ void TestQmlformat::testLargeBindings() QCOMPARE(runQmlformat(testFile("largeBindings.qml"), false, true), readTestFile("largeBindings.formatted.qml")); } +void TestQmlformat::testLineEndings() +{ + // macos + const QString macosContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "macos"); + QVERIFY(!macosContents.contains("\n")); + QVERIFY(macosContents.contains("\r")); + + // windows + const QString windowsContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "windows"); + QVERIFY(windowsContents.contains("\r\n")); + + // unix + const QString unixContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "unix"); + QVERIFY(unixContents.contains("\n")); + QVERIFY(!unixContents.contains("\r")); +} + #if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled void TestQmlformat::testExample_data() { @@ -240,15 +274,22 @@ void TestQmlformat::testExample() } #endif -QString TestQmlformat::runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed) +QString TestQmlformat::runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed, const QString &newlineFormat) { + // Copy test file to temporary location + QTemporaryDir tempDir; + const QString tempFile = tempDir.path() + QDir::separator() + "to_format.qml"; + QFile::copy(fileToFormat, tempFile); + QStringList args; - args << fileToFormat; + args << "-i"; + args << tempFile; if (!sortImports) args << "-n"; - QString output; + args << "-l" << newlineFormat; + auto verify = [&]() { QProcess process; process.start(m_qmlformatPath, args); @@ -256,13 +297,15 @@ QString TestQmlformat::runQmlformat(const QString &fileToFormat, bool sortImport QCOMPARE(process.exitStatus(), QProcess::NormalExit); if (shouldSucceed) QCOMPARE(process.exitCode(), 0); - else - QVERIFY(process.exitCode() != 0); - output = process.readAllStandardOutput(); }; verify(); - return output; + QFile temp(tempFile); + + temp.open(QIODevice::ReadOnly); + QString formatted = QString::fromUtf8(temp.readAll()); + + return formatted; } QTEST_MAIN(TestQmlformat) diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp new file mode 100644 index 0000000000..db080a5cad --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "foreign.h" + +int Foreign::things() const +{ + return m_things; +} + +void Foreign::setThings(int things) +{ + if (m_things == things) + return; + + m_things = things; + emit thingsChanged(m_things); +} diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.h b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h new file mode 100644 index 0000000000..d5e5a060b4 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef FOREIGN_H +#define FOREIGN_H + +#include <QtCore/qobject.h> + +class Foreign : public QObject +{ + Q_OBJECT + Q_PROPERTY(int things READ things WRITE setThings NOTIFY thingsChanged) + +public: + int things() const; + +public slots: + void setThings(int things); + +signals: + void thingsChanged(int things); + +private: + int m_things = 0; +}; + +#endif // FOREIGN_H diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro new file mode 100644 index 0000000000..87521eac43 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro @@ -0,0 +1,10 @@ +TEMPLATE = lib +QT = core + +macos:CONFIG -= app_bundle +CONFIG -= debug_and_release_target + +SOURCES = foreign.cpp +HEADERS = foreign.h + +CONFIG += metatypes static diff --git a/tests/auto/qml/qmltyperegistrar/hppheader.hpp b/tests/auto/qml/qmltyperegistrar/hppheader.hpp new file mode 100644 index 0000000000..f5fc881b77 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/hppheader.hpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef HPPHEADER_HPP +#define HPPHEADER_HPP + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class HppClass : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int eieiei READ eieiei WRITE setEieiei NOTIFY eieieiChanged) + +public: + int eieiei() const + { + return m_eieiei; + } + +public slots: + void setEieiei(int eieiei) + { + if (m_eieiei == eieiei) + return; + + m_eieiei = eieiei; + emit eieieiChanged(m_eieiei); + } + +signals: + void eieieiChanged(int eieiei); + +private: + int m_eieiei; +}; + +#endif // HPPHEADER_HPP diff --git a/tests/auto/qml/qmltyperegistrar/noextheader b/tests/auto/qml/qmltyperegistrar/noextheader new file mode 100644 index 0000000000..3b6cb6d72c --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/noextheader @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef NOEXTHEADER +#define NOEXTHEADER + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class Noext : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int gagaga READ gagaga WRITE setGagaga NOTIFY gagagaChanged) + +public: + int gagaga() const + { + return m_gagaga; + } + +public slots: + void setGagaga(int gagaga) + { + if (m_gagaga == gagaga) + return; + + m_gagaga = gagaga; + emit gagagaChanged(m_gagaga); + } + +signals: + void gagagaChanged(int gagaga); + +private: + int m_gagaga; +}; + +#endif // NOEXTHEADER diff --git a/tests/auto/qml/qmltyperegistrar/qmltyperegistrar.pro b/tests/auto/qml/qmltyperegistrar/qmltyperegistrar.pro new file mode 100644 index 0000000000..738d099123 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/qmltyperegistrar.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = foreign tst_qmltyperegistrar.pro + +tst_qmltyperegistrar_pro.depends = foreign diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp new file mode 100644 index 0000000000..8bdee2f937 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "tst_qmltyperegistrar.h" +#include <QtTest/qtest.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qfile.h> + +void tst_qmltyperegistrar::initTestCase() +{ + QFile file(QCoreApplication::applicationDirPath() + "/tst_qmltyperegistrar.qmltypes"); + QVERIFY(file.open(QIODevice::ReadOnly)); + qmltypesData = file.readAll(); + QVERIFY(file.atEnd()); + QCOMPARE(file.error(), QFile::NoError); +} + +void tst_qmltyperegistrar::qmltypesHasForeign() +{ + QVERIFY(qmltypesData.contains("things")); +} + +void tst_qmltyperegistrar::qmltypesHasHppClassAndNoext() +{ + QVERIFY(qmltypesData.contains("HppClass")); + QVERIFY(qmltypesData.contains("Noext")); +} + +void tst_qmltyperegistrar::qmltypesHasFileNames() +{ + QVERIFY(qmltypesData.contains("file: \"hppheader.hpp\"")); + QVERIFY(qmltypesData.contains("file: \"noextheader\"")); + QVERIFY(qmltypesData.contains("file: \"tst_qmltyperegistrar.h\"")); +} + +QTEST_MAIN(tst_qmltyperegistrar) diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h new file mode 100644 index 0000000000..37d49efa40 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef TST_QMLTYPEREGISTRAR_H +#define TST_QMLTYPEREGISTRAR_H + +#include "foreign.h" + +#include <QtQml/qqml.h> + +class Local : public Foreign +{ + Q_OBJECT + QML_ELEMENT +}; + +class tst_qmltyperegistrar : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void qmltypesHasForeign(); + void qmltypesHasHppClassAndNoext(); + void qmltypesHasFileNames(); + +private: + QByteArray qmltypesData; +}; + +#endif // TST_QMLTYPEREGISTRAR_H diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro new file mode 100644 index 0000000000..fe21b122c2 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro @@ -0,0 +1,20 @@ +TEMPLATE = app + +QT = core qml testlib +CONFIG += testcase qmltypes +CONFIG -= debug_and_release_target +macos:CONFIG -= app_bundle + +SOURCES += tst_qmltyperegistrar.cpp +HEADERS += \ + hppheader.hpp \ + noextheader \ + tst_qmltyperegistrar.h + +QMLTYPES_FILENAME = tst_qmltyperegistrar.qmltypes +QML_FOREIGN_METATYPES += foreign/foreign_metatypes.json +QML_IMPORT_NAME = QmlTypeRegistrarTest +QML_IMPORT_VERSION = 1.0 + +INCLUDEPATH += foreign +LIBS += -Lforeign -lforeign diff --git a/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml b/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml index ee400eb41f..d8e278a7a1 100644 --- a/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml +++ b/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml @@ -3,5 +3,7 @@ import qt.test 1.0 TestItem { property var vector + property var myset positions: vector + barrays: myset } diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index f2fa301565..8bee4ef260 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2464,20 +2464,31 @@ void tst_qqmllanguage::scriptStringJs() QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); } +struct FreeUnitData +{ + static void cleanup(const QV4::CompiledData::Unit *readOnlyQmlUnit) + { + if (readOnlyQmlUnit && !(readOnlyQmlUnit->flags & QV4::CompiledData::Unit::StaticData)) + free(const_cast<QV4::CompiledData::Unit *>(readOnlyQmlUnit)); + } +}; + void tst_qqmllanguage::scriptStringWithoutSourceCode() { QUrl url = testFileUrl("scriptString7.qml"); + QScopedPointer<const QV4::CompiledData::Unit, FreeUnitData> readOnlyQmlUnit; { QQmlEnginePrivate *eng = QQmlEnginePrivate::get(&engine); QQmlRefPointer<QQmlTypeData> td = eng->typeLoader.getType(url); Q_ASSERT(td); - const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->unitData(); + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); + readOnlyQmlUnit.reset(compilationUnit->unitData()); Q_ASSERT(readOnlyQmlUnit); QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize)); - memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize); + memcpy(qmlUnit, readOnlyQmlUnit.data(), readOnlyQmlUnit->unitSize); + qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); compilationUnit->setUnitData(qmlUnit); const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); @@ -5773,25 +5784,31 @@ class TestItem : public QObject { Q_OBJECT Q_PROPERTY( QVector<QPointF> positions MEMBER m_points ) + Q_PROPERTY( QSet<QByteArray> barrays MEMBER m_barrays ) public: TestItem() = default; QVector< QPointF > m_points; + QSet<QByteArray> m_barrays; }; Q_DECLARE_METATYPE(QVector<QPointF>); +Q_DECLARE_METATYPE(QSet<QByteArray>); void tst_qqmllanguage::arrayToContainer() { QQmlEngine engine; qmlRegisterType<TestItem>("qt.test", 1, 0, "TestItem"); QVector<QPointF> points { QPointF (2.0, 3.0) }; + QSet<QByteArray> barrays { QByteArray("hello"), QByteArray("world") }; engine.rootContext()->setContextProperty("test", QVariant::fromValue(points)); QQmlComponent component(&engine, testFileUrl("arrayToContainer.qml")); VERIFY_ERRORS(0); - QScopedPointer<TestItem> root(qobject_cast<TestItem *>(component.createWithInitialProperties( {{"vector", QVariant::fromValue(points)}} ))); + QScopedPointer<TestItem> root(qobject_cast<TestItem *>(component.createWithInitialProperties( {{"vector", QVariant::fromValue(points)}, {"myset", QVariant::fromValue(barrays)} } ))); QVERIFY(root); QCOMPARE(root->m_points.at(0), QPointF (2.0, 3.0) ); + QVERIFY(root->m_barrays.contains("hello")); + QVERIFY(root->m_barrays.contains("world")); } class EnumTester : public QObject diff --git a/tests/auto/quick/qquickanimations/data/signalorder.qml b/tests/auto/quick/qquickanimations/data/signalorder.qml new file mode 100644 index 0000000000..35029b0c84 --- /dev/null +++ b/tests/auto/quick/qquickanimations/data/signalorder.qml @@ -0,0 +1,27 @@ +import QtQuick 2.12 + + +Item { + id: wrapper + width: 400; height: 400 + + Rectangle { + id: greenRect + width: 50; height: 50 + color: "red" + + ColorAnimation on color { + id: colorAnimation + objectName: "ColorAnimation" + to: "green" + duration: 10 + running: false + } + + ParallelAnimation { + id: parallelAnimation + objectName: "ParallelAnimation" + running: false + } + } +} diff --git a/tests/auto/quick/qquickanimations/qquickanimations.pro b/tests/auto/quick/qquickanimations/qquickanimations.pro index 94f694181d..6998c023db 100644 --- a/tests/auto/quick/qquickanimations/qquickanimations.pro +++ b/tests/auto/quick/qquickanimations/qquickanimations.pro @@ -64,6 +64,7 @@ OTHER_FILES += \ data/scriptActionCrash.qml \ data/sequentialAnimationNullChildBug.qml \ data/signals.qml \ + data/signalorder.qml \ data/transitionAssignmentBug.qml \ data/valuesource.qml \ data/valuesource2.qml diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index 55957fa71a..e62d49ed6b 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -89,6 +89,8 @@ private slots: void easingProperties(); void rotation(); void startStopSignals(); + void signalOrder_data(); + void signalOrder(); void runningTrueBug(); void nonTransitionBug(); void registrationBug(); @@ -1323,6 +1325,56 @@ void tst_qquickanimations::startStopSignals() QVERIFY(timer.elapsed() >= 200); } +void tst_qquickanimations::signalOrder_data() +{ + QTest::addColumn<QByteArray>("animationType"); + QTest::addColumn<int>("duration"); + + QTest::addRow("ColorAnimation, duration = 10") << QByteArray("ColorAnimation") << 10; + QTest::addRow("ColorAnimation, duration = 0") << QByteArray("ColorAnimation") << 0; + QTest::addRow("ParallelAnimation, duration = 0") << QByteArray("ParallelAnimation") << 0; +} + +void tst_qquickanimations::signalOrder() +{ + QFETCH(QByteArray, animationType); + QFETCH(int, duration); + + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("signalorder.qml")); + QScopedPointer<QObject> obj(c.create()); + auto *root = qobject_cast<QQuickItem *>(obj.data()); + QVERIFY(root); + QQuickAbstractAnimation *animation = root->findChild<QQuickAbstractAnimation*>(animationType); + + const QVector<void (QQuickAbstractAnimation::*)()> signalsToConnect = { + &QQuickAbstractAnimation::started, + &QQuickAbstractAnimation::stopped, + &QQuickAbstractAnimation::finished + }; + const QVector<const char*> expectedSignalOrder = { + "started", + "stopped", + "finished" + }; + + QVector<const char*> actualSignalOrder; + + for (int i = 0; i < signalsToConnect.size(); ++i) { + const char *str = expectedSignalOrder.at(i); + connect(animation, signalsToConnect.at(i) , [str, &actualSignalOrder] () { + actualSignalOrder.append(str); + }); + } + QSignalSpy finishedSpy(animation, SIGNAL(finished())); + if (QQuickColorAnimation *colorAnimation = qobject_cast<QQuickColorAnimation*>(animation)) + colorAnimation->setDuration(duration); + + animation->start(); + QTRY_VERIFY(finishedSpy.count()); + QCOMPARE(actualSignalOrder, expectedSignalOrder); +} + void tst_qquickanimations::runningTrueBug() { //ensure we start correctly when "running: true" is explicitly set diff --git a/tests/auto/quick/qquickcanvasitem/BLACKLIST b/tests/auto/quick/qquickcanvasitem/BLACKLIST index 21580b6730..a4b25475a4 100644 --- a/tests/auto/quick/qquickcanvasitem/BLACKLIST +++ b/tests/auto/quick/qquickcanvasitem/BLACKLIST @@ -3,12 +3,18 @@ macos [canvas::test_paint] macos [canvas::test_save] +windows macos [canvas::test_implicitlySizedParent] -macos ci +* +# QTBUG-41043 [canvas::test_toDataURL] -macos +* [fillRect::test_fillRect] macos [imagedata::test_rounding] macos ci +[ContextFontValidation::test_pixelSize] +opensuse-leap +[ContextFontValidation::test_valid] +opensuse-leap diff --git a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml index b0fb7fcf8c..5e02ca10d0 100644 --- a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml +++ b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml @@ -7,6 +7,7 @@ TestCase { when:windowShown width:100 height:100 + visible: true property Component component:CanvasComponent{} function cleanupTestCase() { wait(100) //wait for a short while to make sure no leaked textures @@ -19,8 +20,8 @@ TestCase { // { tag:"image cooperative", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Cooperative}}, { tag:"image immediate", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Immediate}}, // { tag:"fbo cooperative", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Cooperative}}, - { tag:"fbo immediate", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Immediate}}, - { tag:"fbo threaded", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Threaded}} + { tag:"fbo immediate", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Immediate}} +// { tag:"fbo threaded", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Threaded}} // QTBUG-82675 ]; return []; } diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml index d9017150a4..ef1b7a7b2a 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml @@ -357,7 +357,7 @@ CanvasTestCase { ctx.fillStyle = '#0f0'; ctx.beginPath(); ctx.moveTo(0, 50); - ctx.translate(100, 0); + ctx.translate(50, 0); ctx.arcTo(50, 50, 50, 0, 50); ctx.lineTo(-100, 0); ctx.fill(); @@ -367,11 +367,11 @@ CanvasTestCase { comparePixel(ctx, 99,0, 0,255,0,255); comparePixel(ctx, 0,25, 0,255,0,255); comparePixel(ctx, 50,25, 0,255,0,255); - comparePixel(ctx, 99,25, 0,255,0,255); + comparePixel(ctx, 99,25, 255,0,0,255); comparePixel(ctx, 0,49, 0,255,0,255); comparePixel(ctx, 50,49, 0,255,0,255); - comparePixel(ctx, 99,49, 0,255,0,255); - } + comparePixel(ctx, 99,49, 255,0,0,255); + } function test_zero(row) { var canvas = createCanvasObject(row); var ctx = canvas.getContext('2d'); diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml index 8238d87313..d74df3daa7 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml @@ -129,12 +129,12 @@ CanvasTestCase { tryCompare(c, "availableChangedCount", 1); c.requestPaint(); - verify(c.save("c.png")); - c.loadImage("c.png"); - wait(200); - verify(c.isImageLoaded("c.png")); - verify(!c.isImageLoading("c.png")); - verify(!c.isImageError("c.png")); + var imagePath = applicationDirPath + "/c.png"; + verify(c.save(imagePath)); + c.loadImage(imagePath); + tryVerify(function() { return c.isImageLoaded(imagePath) }) + verify(!c.isImageLoading(imagePath)); + verify(!c.isImageError(imagePath)); c.destroy(); } @@ -187,28 +187,28 @@ CanvasTestCase { tryCompare(c, "availableChangedCount", 1); //scene graph could be available immediately //in this case, we force waiting a short while until the init paint finished - tryCompare(c, "paintedCount", 1); + tryCompare(c, "paintedCount", 0); ctx.fillRect(0, 0, c.width, c.height); c.toDataURL(); - tryCompare(c, "paintedCount", 2); + tryCompare(c, "paintedCount", 1); tryCompare(c, "paintCount", 1); // implicit repaint when visible and resized testCase.visible = true; c.width += 1; c.height += 1; tryCompare(c, "paintCount", 2); - tryCompare(c, "paintedCount", 2); + tryCompare(c, "paintedCount", 1); // allow explicit repaint even when hidden testCase.visible = false; c.requestPaint(); tryCompare(c, "paintCount", 3); - tryCompare(c, "paintedCount", 2); + tryCompare(c, "paintedCount", 1); // no implicit repaint when resized but hidden c.width += 1; c.height += 1; waitForRendering(c); compare(c.paintCount, 3); - tryCompare(c, "paintedCount", 2); + tryCompare(c, "paintedCount", 1); c.destroy(); } function test_loadImage(row) { @@ -221,8 +221,7 @@ CanvasTestCase { verify(!c.isImageLoaded("red.png")); c.loadImage("red.png"); - wait(200); - verify(c.isImageLoaded("red.png")); + tryVerify(function() { return c.isImageLoaded("red.png") }); verify(!c.isImageLoading("red.png")); verify(!c.isImageError("red.png")); diff --git a/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro b/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro index 70e5a05f8d..90c7962382 100644 --- a/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro +++ b/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro @@ -55,5 +55,3 @@ OTHER_FILES += \ data/yellow75.png \ data/tst_invalidContext.qml - -CONFIG += insignificant_test # QTBUG-41043 diff --git a/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp b/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp index dad8df0682..4a83bd6c0d 100644 --- a/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp +++ b/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp @@ -46,6 +46,8 @@ public slots: false #endif )); + engine->rootContext()->setContextProperty("applicationDirPath", + QCoreApplication::applicationDirPath()); } }; diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml index 0732884c97..f61e2e0f48 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml @@ -118,10 +118,10 @@ Item { function test_warnAboutLayoutItemsWithAnchors() { - var fullPath = Qt.resolvedUrl("tst_rowlayout.qml") + var regex = new RegExp("QML Item: Detected anchors on an item that is managed by a layout. " + + "This is undefined behavior; use Layout.alignment instead.") for (var i = 0; i < 7; ++i) { - ignoreWarning(fullPath + ":" + (75 + 5*i) +":17: QML Item: Detected anchors on an item that is managed by a layout. " - + "This is undefined behavior; use Layout.alignment instead.") + ignoreWarning(regex) } var layout = itemsWithAnchorsLayout_Component.createObject(container) waitForRendering(layout) diff --git a/tests/auto/quick/qquickmousearea/data/settingHiddenInPressUngrabs.qml b/tests/auto/quick/qquickmousearea/data/settingHiddenInPressUngrabs.qml new file mode 100644 index 0000000000..4532dabfd8 --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/settingHiddenInPressUngrabs.qml @@ -0,0 +1,36 @@ +import QtQuick 2.11 +import QtQuick.Window 2.11 + +Window { + id: window + visible: true + width: 200 + height: 200 + + MouseArea { + objectName: "cat" + anchors.fill: parent + onPressed: visible = false + width: parent.width / 2 + height: parent.height + Rectangle { + width: 10 + height: 10 + color: "blue" + } + } + MouseArea { + objectName: "mouse" + x: parent.width / 2 + width: parent.width / 2 + height: parent.height + onPressed: { + enabled = false + } + Rectangle { + width: 10 + height: 10 + color: "blue" + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 3503e8a9fd..5879fc6897 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -158,6 +158,7 @@ private slots: void pressOneAndTapAnother(); void mask(); void nestedEventDelivery(); + void settingHiddenInPressUngrabs(); private: int startDragDistance() const { @@ -2346,6 +2347,43 @@ void tst_QQuickMouseArea::nestedEventDelivery() // QTBUG-70898 QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(50,150)); } +void tst_QQuickMouseArea::settingHiddenInPressUngrabs() +{ + // When an item sets itself hidden, while handling pressed, it doesn't receive the grab. + // But that in turn means it doesn't see any release events, so we need to make sure it + // receives an ungrab event. + + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("settingHiddenInPressUngrabs.qml")); + QScopedPointer<QQuickWindow> window(qmlobject_cast<QQuickWindow *>(c.create())); + QVERIFY(window.data()); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickMouseArea *catArea = window->findChild<QQuickMouseArea*>("cat"); + QVERIFY(catArea != nullptr); + auto pointOnCatArea = catArea->mapToScene(QPointF(5.0, 5.0)).toPoint(); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, pointOnCatArea); + + QCoreApplication::processEvents(); + // The click hides the cat area + QTRY_VERIFY(!catArea->isVisible()); + // The cat area is not stuck in pressed state. + QVERIFY(!catArea->pressed()); + + QQuickMouseArea *mouseArea = window->findChild<QQuickMouseArea*>("mouse"); + QVERIFY(mouseArea != nullptr); + auto pointOnMouseArea = mouseArea->mapToScene(QPointF(5.0, 5.0)).toPoint(); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, pointOnMouseArea); + + QCoreApplication::processEvents(); + // The click disables the mouse area + QTRY_VERIFY(!mouseArea->isEnabled()); + // The mouse area is not stuck in pressed state. + QVERIFY(!mouseArea->pressed()); +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" diff --git a/tests/auto/quickwidgets/qquickwidget/data/resizeOverlay.qml b/tests/auto/quickwidgets/qquickwidget/data/resizeOverlay.qml new file mode 100644 index 0000000000..5547856941 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/resizeOverlay.qml @@ -0,0 +1,15 @@ +import QtQuick 2.15 + +import Test 1.0 + +Item { + id: root + + property alias overlay: overlay + + Overlay { + id: overlay + // Parent it to the contentItem of the underlying QQuickWindow. + parent: root.parent + } +} diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 691dfd1bc6..12c51caa75 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -33,6 +33,7 @@ #include <QtQml/qqmlcontext.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickitem_p.h> #include "../../shared/util.h" #include <QtGui/QWindow> #include <QtGui/QScreen> @@ -142,6 +143,7 @@ private slots: void synthMouseFromTouch_data(); void synthMouseFromTouch(); void tabKey(); + void resizeOverlay(); private: QTouchDevice *device = QTest::createTouchDevice(); @@ -662,6 +664,74 @@ void tst_qquickwidget::tabKey() QVERIFY(middleItem->property("activeFocus").toBool()); } +class Overlay : public QQuickItem, public QQuickItemChangeListener +{ + Q_OBJECT + +public: + Overlay() = default; + + ~Overlay() + { + QQuickItemPrivate::get(parentItem())->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + } + + // componentCompleted() is too early to add the listener, as parentItem() + // is still null by that stage, so we use this function instead. + void startListening() + { + QQuickItemPrivate::get(parentItem())->addItemChangeListener(this, QQuickItemPrivate::Geometry); + } + +private: + virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &/*oldGeometry*/) override + { + auto window = QQuickItemPrivate::get(this)->window; + if (!window) + return; + + setSize(window->size()); + } +}; + +// Test that an item that resizes itself based on the window size can use a +// Geometry item change listener to respond to changes in size. This is a +// simplified test to mimic a use case involving Overlay from Qt Quick Controls 2. +void tst_qquickwidget::resizeOverlay() +{ + QWidget widget; + auto contentVerticalLayout = new QVBoxLayout(&widget); + contentVerticalLayout->setMargin(0); + + qmlRegisterType<Overlay>("Test", 1, 0, "Overlay"); + + auto quickWidget = new QQuickWidget(testFileUrl("resizeOverlay.qml"), &widget); + QCOMPARE(quickWidget->status(), QQuickWidget::Ready); + quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + contentVerticalLayout->addWidget(quickWidget); + + auto rootItem = qobject_cast<QQuickItem*>(quickWidget->rootObject()); + QVERIFY(rootItem); + + auto overlay = rootItem->property("overlay").value<Overlay*>(); + QVERIFY(overlay); + QVERIFY(overlay->parentItem()); + overlay->startListening(); + + widget.resize(200, 200); + widget.show(); + QCOMPARE(rootItem->width(), 200); + QCOMPARE(rootItem->height(), 200); + QCOMPARE(overlay->width(), rootItem->width()); + QCOMPARE(overlay->height(), rootItem->height()); + + widget.resize(300, 300); + QCOMPARE(rootItem->width(), 300); + QCOMPARE(rootItem->height(), 300); + QCOMPARE(overlay->width(), rootItem->width()); + QCOMPARE(overlay->height(), rootItem->height()); +} + QTEST_MAIN(tst_qquickwidget) #include "tst_qquickwidget.moc" diff --git a/tools/qmlformat/commentastvisitor.cpp b/tools/qmlformat/commentastvisitor.cpp index 4dd241ff93..9383fa29aa 100644 --- a/tools/qmlformat/commentastvisitor.cpp +++ b/tools/qmlformat/commentastvisitor.cpp @@ -126,6 +126,14 @@ Comment CommentAstVisitor::findComment(SourceLocation first, SourceLocation last return Comment(m_engine, Comment::Location::Front, comments); } + if (locations & Comment::Location::Front_Inline) { + quint32 searchAt = first.startLine; + + const auto comments = findCommentsInLine(searchAt); + if (!comments.isEmpty()) + return Comment(m_engine, Comment::Location::Front_Inline, comments); + } + if (locations & Comment::Location::Back_Inline) { quint32 searchAt = last.startLine; @@ -198,13 +206,13 @@ void CommentAstVisitor::endVisit(StatementList *node) bool CommentAstVisitor::visit(UiObjectBinding *node) { - attachComment(node, Comment::Front | Comment::Back); + attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back); return true; } bool CommentAstVisitor::visit(UiObjectDefinition *node) { - attachComment(node, Comment::Front | Comment::Back); + attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back); return true; } diff --git a/tools/qmlformat/commentastvisitor.h b/tools/qmlformat/commentastvisitor.h index d3de0a9b9d..21c7eb6416 100644 --- a/tools/qmlformat/commentastvisitor.h +++ b/tools/qmlformat/commentastvisitor.h @@ -45,10 +45,11 @@ struct Comment enum Location : int { Front = 1, - Back = Front << 1, + Front_Inline = Front << 1, + Back = Front_Inline << 1, Back_Inline = Back << 1, DefaultLocations = Front | Back_Inline, - AllLocations = Front | Back | Back_Inline + AllLocations = Front | Back | Front_Inline | Back_Inline } m_location = Front; Comment() = default; diff --git a/tools/qmlformat/dumpastvisitor.cpp b/tools/qmlformat/dumpastvisitor.cpp index 75712975cc..e0feff4e51 100644 --- a/tools/qmlformat/dumpastvisitor.cpp +++ b/tools/qmlformat/dumpastvisitor.cpp @@ -626,7 +626,7 @@ QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, case Node::Kind_IfStatement: { auto *ifStatement = cast<IfStatement *>(statement); - m_blockNeededBraces = false; + m_blockNeededBraces = !blockAllowBraceless; QString ifFalse = parseStatement(ifStatement->ko, false, true); QString ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), true); @@ -641,8 +641,36 @@ QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), false); } + if (ifStatement->ok->kind != Node::Kind_Block) + ifTrue += ";"; + + if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) + ifFalse += ";"; + QString result = "if (" + parseExpression(ifStatement->expression) + ")"; + if (m_blockNeededBraces) { + if (ifStatement->ok->kind != Node::Kind_Block) { + QString result = "{\n"; + m_indentLevel++; + result += formatLine(ifTrue); + m_indentLevel--; + result += formatLine("} ", false); + ifTrue = result; + ifTrueBlock = true; + } + + if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) { + QString result = "{\n"; + m_indentLevel++; + result += formatLine(ifFalse); + m_indentLevel--; + result += formatLine("} ", false); + ifFalse = result; + ifFalseBlock = true; + } + } + if (ifTrueBlock) { result += " " + ifTrue; } else { @@ -724,8 +752,6 @@ QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, else result += ";"; - - return result; } case Node::Kind_WhileStatement: { @@ -825,7 +851,7 @@ QString DumpAstVisitor::parseStatementList(StatementList *list) result += getOrphanedComments(list); for (auto *item = list; item != nullptr; item = item->next) { - QString statement = parseStatement(item->statement->statementCast()); + QString statement = parseStatement(item->statement->statementCast(), false, true); if (statement.isEmpty()) continue; @@ -843,8 +869,8 @@ QString DumpAstVisitor::parseStatementList(StatementList *list) } bool DumpAstVisitor::visit(UiPublicMember *node) { - addLine(getComment(node, Comment::Location::Front)); + QString commentFront = getComment(node, Comment::Location::Front); QString commentBackInline = getComment(node, Comment::Location::Back_Inline); switch (node->type) @@ -859,6 +885,7 @@ bool DumpAstVisitor::visit(UiPublicMember *node) { scope().m_firstSignal = false; } + addLine(commentFront); addLine("signal "+node->name.toString()+"("+parseUiParameterList(node->parameters) + ")" + commentBackInline); break; @@ -897,6 +924,7 @@ bool DumpAstVisitor::visit(UiPublicMember *node) { if (has_type_modifier) member_type = node->typeModifier + "<" + member_type + ">"; + addLine(commentFront); if (is_readonly && statement.isEmpty() && scope().m_bindings.contains(node->name.toString())) { m_result += formatLine(prefix + "property " + member_type + " ", false); @@ -1003,6 +1031,7 @@ bool DumpAstVisitor::visit(UiObjectDefinition *node) { } addLine(getComment(node, Comment::Location::Front)); + addLine(getComment(node, Comment::Location::Front_Inline)); addLine(parseUiQualifiedId(node->qualifiedTypeNameId) + " {"); m_indentLevel++; @@ -1198,6 +1227,7 @@ bool DumpAstVisitor::visit(UiObjectBinding *node) { } else { addNewLine(); addLine(getComment(node, Comment::Location::Front)); + addLine(getComment(node, Comment::Location::Front_Inline)); addLine(result + " {"); } diff --git a/tools/qmlformat/main.cpp b/tools/qmlformat/main.cpp index da58ffd5d0..3e71110cf9 100644 --- a/tools/qmlformat/main.cpp +++ b/tools/qmlformat/main.cpp @@ -43,7 +43,7 @@ #include "dumpastvisitor.h" #include "restructureastvisitor.h" -bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImports, bool force) +bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImports, bool force, const QString& newline) { QFile file(filename); @@ -128,11 +128,27 @@ bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImp } } + + const bool native = newline == "native"; + + if (!native) { + if (newline == "macos") { + dumpCode = dumpCode.replace("\n","\r"); + } else if (newline == "windows") { + dumpCode = dumpCode.replace("\n", "\r\n"); + } else if (newline == "unix") { + // Nothing needs to be done for unix line-endings + } else { + qWarning().noquote() << "Unknown line ending type" << newline; + return false; + } + } + if (inplace) { if (verbose) qWarning().noquote() << "Writing to file" << filename; - if (!file.open(QIODevice::Text | QIODevice::WriteOnly)) + if (!file.open(native ? QIODevice::WriteOnly | QIODevice::Text : QIODevice::WriteOnly)) { qWarning().noquote() << "Failed to open" << filename << "for writing"; return false; @@ -141,7 +157,9 @@ bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImp file.write(dumpCode.toUtf8()); file.close(); } else { - QTextStream(stdout) << dumpCode; + QFile out; + out.open(stdout, QIODevice::WriteOnly); + out.write(dumpCode.toUtf8()); } return true; @@ -172,6 +190,10 @@ int main(int argc, char *argv[]) parser.addOption(QCommandLineOption({"f", "force"}, QStringLiteral("Continue even if an error has occurred."))); + parser.addOption(QCommandLineOption({"l", "newline"}, + QStringLiteral("Override the new line format to use (native macos unix windows)."), + "newline", "native")); + parser.addPositionalArgument("filenames", "files to be processed by qmlformat"); parser.process(app); @@ -181,8 +203,13 @@ int main(int argc, char *argv[]) if (positionalArguments.isEmpty()) parser.showHelp(-1); + if (!parser.isSet("inplace") && parser.value("newline") != "native") { + qWarning() << "Error: The -l option can only be used with -i"; + return -1; + } + for (const QString& file: parser.positionalArguments()) { - if (!parseFile(file, parser.isSet("inplace"), parser.isSet("verbose"), !parser.isSet("no-sort"), parser.isSet("force"))) + if (!parseFile(file, parser.isSet("inplace"), parser.isSet("verbose"), !parser.isSet("no-sort"), parser.isSet("force"), parser.value("newline"))) success = false; } #endif diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index baae3a3ce7..604cf49122 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -183,8 +183,6 @@ QStringList completeImportPaths(const QString &uri, const QString &basePath, QTy } static const QLatin1String SlashQmldir = QLatin1String("/qmldir"); -static const QLatin1String SlashAppDotQmltypes = QLatin1String("/app.qmltypes"); -static const QLatin1String SlashLibDotQmltypes = QLatin1String("/lib.qmltypes"); static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes"); void FindUnqualifiedIDVisitor::readQmltypes(const QString &filename, @@ -287,16 +285,29 @@ void FindUnqualifiedIDVisitor::importHelper(const QString &module, const QString break; } - Import result; - if (QFile::exists(qmltypesPath + SlashAppDotQmltypes)) - readQmltypes(qmltypesPath + SlashAppDotQmltypes, result); - else if (QFile::exists(qmltypesPath + SlashLibDotQmltypes)) - readQmltypes(qmltypesPath + SlashLibDotQmltypes, result); - else + if (!m_qmltypeFiles.isEmpty()) continue; + + Import result; + + QDirIterator it { qmltypesPath, QStringList() << QLatin1String("*.qmltypes"), QDir::Files }; + + while (it.hasNext()) + readQmltypes(it.next(), result); + processImport(prefix, result); } } + + if (!m_qmltypeFiles.isEmpty()) + { + Import result; + + for (const auto &qmltypeFile : m_qmltypeFiles) + readQmltypes(qmltypeFile, result); + + processImport("", result); + } } ScopeTree::Ptr FindUnqualifiedIDVisitor::localFile2ScopeTree(const QString &filePath) @@ -433,6 +444,17 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *) m_colorOut.writeUncolored(reader.errorMessage()); } } + + if (!m_qmltypeFiles.isEmpty()) + { + for (const auto &qmltypeFile : m_qmltypeFiles) { + auto reader = createQmltypesReaderForFile(qmltypeFile); + auto succ = reader(&objects, &dependencies); + if (!succ) + m_colorOut.writeUncolored(reader.errorMessage()); + } + } + // add builtins for (auto objectIt = objects.begin(); objectIt != objects.end(); ++objectIt) { auto val = objectIt.value(); @@ -644,10 +666,11 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp) return true; } -FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QString code, +FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QStringList qmltypeFiles, QString code, QString fileName, bool silent) : m_rootScope(ScopeTree::create(ScopeType::JSFunctionScope, "global")), m_qmltypeDirs(std::move(qmltypeDirs)), + m_qmltypeFiles(std::move(qmltypeFiles)), m_code(std::move(code)), m_rootId(QLatin1String("<id>")), m_filePath(std::move(fileName)), diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h index 1c1751fafb..1782012424 100644 --- a/tools/qmllint/findunqualified.h +++ b/tools/qmllint/findunqualified.h @@ -52,7 +52,7 @@ class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor { Q_DISABLE_COPY_MOVE(FindUnqualifiedIDVisitor) public: - explicit FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QString code, + explicit FindUnqualifiedIDVisitor(QStringList qmltypeDirs, QStringList qmltypeFiles, QString code, QString fileName, bool silent); ~FindUnqualifiedIDVisitor() override = default; bool check(); @@ -69,6 +69,7 @@ private: QHash<QString, ScopeTree::ConstPtr> m_types; QHash<QString, ScopeTree::ConstPtr> m_exportedName2Scope; QStringList m_qmltypeDirs; + QStringList m_qmltypeFiles; QString m_code; QHash<QString, ScopeTree::ConstPtr> m_qmlid2scope; QString m_rootId; diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index fa601986b2..05519fc8d8 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -48,7 +48,7 @@ #endif static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualied, - const QStringList &qmltypeDirs) + const QStringList &qmltypeDirs, const QStringList &qmltypeFiles) { QFile file(filename); if (!file.open(QFile::ReadOnly)) { @@ -84,7 +84,7 @@ static bool lint_file(const QString &filename, const bool silent, const bool war if (success && !isJavaScript && warnUnqualied) { auto root = parser.rootNode(); - FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename, silent }; + FindUnqualifiedIDVisitor v { qmltypeDirs, qmltypeFiles, code, filename, silent }; root->accept(&v); success = v.check(); } @@ -118,6 +118,13 @@ int main(int argv, char *argc[]) QLatin1String("directory")); parser.addOption(qmltypesDirsOption); + QCommandLineOption qmltypesFilesOption( + QStringList() << "i" + << "qmltypes", + QLatin1String("Include the specified qmltypes files"), + QLatin1String("qmltypes")); + parser.addOption(qmltypesFilesOption); + parser.addPositionalArgument(QLatin1String("files"), QLatin1String("list of qml or js files to verify")); @@ -134,15 +141,20 @@ int main(int argv, char *argc[]) QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption) ? parser.values(qmltypesDirsOption) # ifndef QT_BOOTSTRAPPED - : QStringList { QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath), - QLatin1String(".") }; + : QStringList { QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) }; # else - : QStringList { QLatin1String(".") }; + : QStringList {}; # endif + + if (!parser.isSet(qmltypesFilesOption)) + qmltypeDirs << "."; + + QStringList qmltypeFiles = parser.isSet(qmltypesFilesOption) ? parser.values(qmltypesFilesOption) : QStringList {}; #else bool silent = false; bool warnUnqualified = false; QStringList qmltypeDirs {}; + QStringList qmltypeFiles {}; #endif bool success = true; #if QT_CONFIG(commandlineparser) @@ -151,7 +163,7 @@ int main(int argv, char *argc[]) const auto arguments = app.arguments(); for (const QString &filename : arguments) #endif - success &= lint_file(filename, silent, warnUnqualified, qmltypeDirs); + success &= lint_file(filename, silent, warnUnqualified, qmltypeDirs, qmltypeFiles); return success ? 0 : -1; } |