diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | src/imports/statemachine/signaltransition.cpp | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache.cpp | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmltypewrapper.cpp | 6 | ||||
-rw-r--r-- | src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc | 27 | ||||
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 8 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p_p.h | 1 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgdefaultglyphnode_p.cpp | 4 | ||||
-rw-r--r-- | src/quickshapes/qquickshapegenericrenderer.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 12 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/Broken.qml | 5 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/asBroken.qml | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 20 | ||||
-rw-r--r-- | tests/auto/quick/qquickflickable/BLACKLIST | 3 | ||||
-rw-r--r-- | tests/auto/quick/qquickflickable/tst_qquickflickable.cpp | 8 | ||||
-rw-r--r-- | tools/qmlimportscanner/main.cpp | 66 |
18 files changed, 113 insertions, 78 deletions
diff --git a/.qmake.conf b/.qmake.conf index 703f7df915..65ef03633e 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -4,4 +4,4 @@ CONFIG += warning_clean DEFINES += QT_NO_LINKED_LIST DEFINES += QT_NO_JAVA_STYLE_ITERATORS -MODULE_VERSION = 5.15.12 +MODULE_VERSION = 5.15.13 diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index eb7095bc2a..dcebb21b41 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -171,7 +171,11 @@ void SignalTransition::connectTriggered() QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle(); QV4::Scope scope(jsEngine); QV4::Scoped<QV4::QObjectMethod> qobjectSignal(scope, QJSValuePrivate::convertedToValue(jsEngine, m_signal)); - Q_ASSERT(qobjectSignal); + if (!qobjectSignal) { + m_signalExpression.take(nullptr); + return; + } + QMetaMethod metaMethod = target->metaObject()->method(qobjectSignal->methodIndex()); int signalIndex = QMetaObjectPrivate::signalIndex(metaMethod); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 5fc94b9ddd..cf2e4ec1dd 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1530,6 +1530,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) if (done->booleanValue()) break; ++argCount; + constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements + if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) { + scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator")); + return { nullptr, 0 }; + } v = scope.alloc<Scope::Uninitialized>(); } } diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 6b68a2a288..8a0abf8bdd 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -51,8 +51,8 @@ #include <QtCore/qdebug.h> #include <QtCore/QCryptographicHash> +#include <QtCore/private/qtools_p.h> -#include <ctype.h> // for toupper #include <limits.h> #include <algorithm> @@ -571,7 +571,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, QVarLengthArray<char, 128> str(length+3); str[0] = 'o'; str[1] = 'n'; - str[2] = toupper(rawName[0]); + str[2] = QtMiscUtils::toAsciiUpper(rawName[0]); if (length > 1) memcpy(&str[3], &rawName[1], length - 1); str[length + 2] = '\0'; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 175de8b936..a6ba4b8cb3 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -419,8 +419,10 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const return Encode(false); QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); - ExecutableCompilationUnit *cu = td->compilationUnit(); - myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + if (ExecutableCompilationUnit *cu = td->compilationUnit()) + myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + else + return Encode(false); // It seems myQmlType has some errors, so we could not compile it. } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); } diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index abfff7cc11..8cd51a50c6 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -52,11 +52,16 @@ options that align with the latest UI design trends. If these UI controls do not satisfy your application's needs, only then it is recommended to create a custom control. +You can use the controls when you design UIs in Qt Design Studio. In addition, +it provides timeline-based animations, visual effects, layouts, and a +live-preview for prototyping applications. \section2 Related Information \list \li \l{Qt Quick Controls} +\li \l{Customizing Qt Quick Controls} \li \l{Qt Quick} +\li \l{Qt Design Studio Manual} \endlist \omit @@ -146,7 +151,7 @@ specific file managed by the resource system. For example, if we wanted to give \li \l{The Qt Resource System} \endlist -\section1 Separate UI from Logic +\section1 Separate UI from Business Logic One of the key goals that most application developers want to achieve is to create a maintainable application. One of the ways to achieve this goal is @@ -162,8 +167,8 @@ reasons why an application's UI should be written in QML: \li JavaScript can easily be used in QML to respond to events. \endlist -Being a strongly typed language, C++ is best suited for an application's logic. -Typically, such code performs tasks such as complex calculations +Being a strongly typed language, C++ is best suited for an application's +business logic. Typically, such code performs tasks such as complex calculations or data processing, which are faster in C++ than QML. Qt offers various approaches to integrate QML and C++ code in an application. @@ -326,6 +331,22 @@ see \l {Choosing the Correct Integration Method Between C++ and QML}. \li \l{Qt Quick Controls - Chat Tutorial}{Chat application tutorial} \endlist +\section1 Using Qt Design Studio + +Qt Design Studio uses UI files that have the filename extension \e {.ui.qml} +to separate the visual parts of the UI from the UI logic you implement in +\e {.qml} files. You should edit UI files only in the \uicontrol {2D} view in +Qt Design Studio. If you use some other tool to add code that Qt Design Studio +does not support, it displays error messages. Fix the errors to enable visual +editing of the UI files again. Typically, you should move the unsupported code +to a \e {.qml} file. + +\section2 Related Information + +\list + \li \l{Qt Design Studio: UI Files} +\endlist + \section1 Using Qt Quick Layouts Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout. diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index ea357d819d..2634b68248 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -2120,11 +2120,9 @@ void QQuickFlickable::setContentWidth(qreal w) d->contentItem->setWidth(w); d->hData.markExtentsDirty(); // Make sure that we're entirely in view. - if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->hData.dragging) { - d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging; + if (!d->pressed && !d->hData.moving && !d->vData.moving) { d->fixupMode = QQuickFlickablePrivate::Immediate; d->fixupX(); - d->hData.contentPositionChangedExternallyDuringDrag = false; } else if (!d->pressed && d->hData.fixingUp) { d->fixupMode = QQuickFlickablePrivate::ExtentChanged; d->fixupX(); @@ -2151,11 +2149,9 @@ void QQuickFlickable::setContentHeight(qreal h) d->contentItem->setHeight(h); d->vData.markExtentsDirty(); // Make sure that we're entirely in view. - if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->vData.dragging) { - d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging; + if (!d->pressed && !d->hData.moving && !d->vData.moving) { d->fixupMode = QQuickFlickablePrivate::Immediate; d->fixupY(); - d->vData.contentPositionChangedExternallyDuringDrag = false; } else if (!d->pressed && d->vData.fixingUp) { d->fixupMode = QQuickFlickablePrivate::ExtentChanged; d->fixupY(); diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index d5d838eaea..aef15e150a 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -120,6 +120,7 @@ public: dragStartOffset = 0; fixingUp = false; inOvershoot = false; + contentPositionChangedExternallyDuringDrag = false; } void markExtentsDirty() { diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index 10fd2c094d..d6834b554e 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -842,9 +842,11 @@ void QSGTextMaskMaterial::populate(const QPointF &p, bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions(); for (int i=0; i<glyphIndexes.size(); ++i) { QPointF glyphPosition = glyphPositions.at(i) + position; + QFixedPoint fixedPointPosition = fixedPointPositions.at(i); + QFixed subPixelPosition; if (supportsSubPixelPositions) - subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPosition.x() * glyphCacheScaleX)); + subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(fixedPointPosition.x.toReal() * glyphCacheScaleX)); QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition); const QTextureGlyphCache::Coord &c = cache->coords.value(glyph); diff --git a/src/quickshapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp index ff840eabaf..98170be6d9 100644 --- a/src/quickshapes/qquickshapegenericrenderer.cpp +++ b/src/quickshapes/qquickshapegenericrenderer.cpp @@ -188,8 +188,12 @@ void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path) void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color) { ShapePathData &d(m_sp[index]); + const bool wasTransparent = d.strokeColor.a == 0; d.strokeColor = colorToColor4ub(color); + const bool isTransparent = d.strokeColor.a == 0; d.syncDirty |= DirtyColor; + if (wasTransparent && !isTransparent) + d.syncDirty |= DirtyStrokeGeom; } void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w) diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp index 9fe2de5368..2b6145030b 100644 --- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp +++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp @@ -393,9 +393,7 @@ void Test262Runner::loadTestExpectations() return; } - int line = 0; while (!file.atEnd()) { - ++line; QByteArray line = file.readLine().trimmed(); if (line.startsWith('#') || line.isEmpty()) continue; @@ -440,9 +438,7 @@ void Test262Runner::updateTestExpectations() QTemporaryFile updatedExpectations; updatedExpectations.open(); - int line = 0; while (!file.atEnd()) { - ++line; QByteArray originalLine = file.readLine(); QByteArray line = originalLine.trimmed(); if (line.startsWith('#') || line.isEmpty()) { diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 363070d7f8..f1c34e6142 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -271,6 +271,8 @@ private slots: void uiLanguage(); void forOfAndGc(); + void spreadNoOverflow(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -5339,6 +5341,16 @@ void tst_QJSEngine::forOfAndGc() QTRY_VERIFY(o->property("count").toInt() > 32768); } +void tst_QJSEngine::spreadNoOverflow() +{ + QJSEngine engine; + + const QString program = QString::fromLatin1("var a = [] ;a.length = 555840;Math.max(...a)"); + const QJSValue result = engine.evaluate(program); + QVERIFY(result.isError()); + QCOMPARE(result.errorType(), QJSValue::RangeError); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qqmllanguage/data/Broken.qml b/tests/auto/qml/qqmllanguage/data/Broken.qml new file mode 100644 index 0000000000..e1b61f31f4 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/Broken.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + notThere: 5 +} diff --git a/tests/auto/qml/qqmllanguage/data/asBroken.qml b/tests/auto/qml/qqmllanguage/data/asBroken.qml new file mode 100644 index 0000000000..bd88d14c76 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/asBroken.qml @@ -0,0 +1,6 @@ +import QtQml 2.15 + +QtObject { + id: self + property var selfAsBroken: self as Broken +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index ac6634290a..92d0069ce4 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -343,6 +343,8 @@ private slots: void bindingAliasToComponentUrl(); void badGroupedProperty(); + void objectAsBroken(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -5951,6 +5953,24 @@ void tst_qqmllanguage::badGroupedProperty() .arg(url.toString())); } +void tst_qqmllanguage::objectAsBroken() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("asBroken.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QVariant selfAsBroken = o->property("selfAsBroken"); + QVERIFY(selfAsBroken.isValid()); + + // 5.15 doesn't enforce type annotation. So the "as" cast succeeds even though + // the target type cannot be resolved. + QCOMPARE(selfAsBroken.value<QObject *>(), o.data()); + + QQmlComponent b(&engine, testFileUrl("Broken.qml")); + QVERIFY(b.isError()); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/quick/qquickflickable/BLACKLIST b/tests/auto/quick/qquickflickable/BLACKLIST deleted file mode 100644 index 636aef4904..0000000000 --- a/tests/auto/quick/qquickflickable/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -# See qtbase/src/testlib/qtestblacklist.cpp for format -[setContentPositionWhileDragging] -ci macos # QTBUG-106278 diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index d092cd0170..62f7c67dd4 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -2642,7 +2642,12 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966 } else if (newExtent >= 0) { // ...or reduce the content size be be less than current (contentX, contentY) position // This forces the content item to move. - expectedContentPos = moveDelta; + // contentY: 150 + // 320 - 150 = 170 pixels down to bottom + // Now reduce contentHeight to 200 + // since we are at the bottom, and the flickable is 100 pixels tall, contentY must land + // at newExtent - 100. + if (isHorizontal) { flickable->setContentWidth(newExtent); } else { @@ -2652,6 +2657,7 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966 // We therefore cannot scroll/flick it further down. Drag it up towards the top instead // (by moving mouse down). pos += moveDelta; + expectedContentPos = unitDelta * (newExtent - (isHorizontal ? flickable->width() : flickable->height())); } QTest::mouseMove(window.data(), pos); diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 37368bb794..8209236916 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -71,14 +71,6 @@ inline QString dependenciesLiteral() { return QStringLiteral("dependencies"); } inline QString moduleLiteral() { return QStringLiteral("module"); } inline QString javascriptLiteral() { return QStringLiteral("javascript"); } inline QString directoryLiteral() { return QStringLiteral("directory"); } -inline QString componentsLiteral() -{ - return QStringLiteral("components"); -} -inline QString scriptsLiteral() -{ - return QStringLiteral("scripts"); -} void printUsage(const QString &appNameIn) { @@ -158,42 +150,21 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { QString plugins; QString classnames; QStringList dependencies; - QStringList componentFiles; - QStringList scriptFiles; QByteArray line; do { line = qmldirFile.readLine(); - const QList<QByteArray> sections = line.split(' '); - if (sections.size() > 0) { - const QByteArray §ionType = sections.at(0); - if (sectionType == "plugin") { - plugins += QString::fromUtf8(line.split(' ').at(1)); - plugins += QLatin1Char(' '); - } else if (sectionType == "classname") { - classnames += QString::fromUtf8(line.split(' ').at(1)); - classnames += QLatin1Char(' '); - } else if (sectionType == "depends") { - if (sections.size() != 3) - std::cerr << "depends: expected 2 arguments: module identifier and version" - << std::endl; - else - dependencies << QString::fromUtf8(sections.at(1)) + QLatin1Char(' ') - + QString::fromUtf8(sections.at(2)).simplified(); - } else if (sections.size() == 2 && sectionType != "module" - && sectionType != "typeinfo") { - componentFiles.append(modulePath + QLatin1Char('/') - + QString::fromUtf8(sections.at(1)).trimmed()); - } else if (sections.size() == 3 - || (sectionType == "singleton" && sections.size() == 4)) { - int fileNameIndex = (sectionType == "singleton") ? 3 : 2; - const QString fileName = QString::fromUtf8(sections.at(fileNameIndex)).trimmed(); - const QString filePath = modulePath + QLatin1Char('/') + fileName; - if (fileName.endsWith(QLatin1String(".js")) - || fileName.endsWith(QLatin1String(".mjs"))) - scriptFiles.append(filePath); - else - componentFiles.append(filePath); - } + if (line.startsWith("plugin")) { + plugins += QString::fromUtf8(line.split(' ').at(1)); + plugins += QLatin1Char(' '); + } else if (line.startsWith("classname")) { + classnames += QString::fromUtf8(line.split(' ').at(1)); + classnames += QLatin1Char(' '); + } else if (line.startsWith("depends")) { + const QList<QByteArray> dep = line.split(' '); + if (dep.length() != 3) + std::cerr << "depends: expected 2 arguments: module identifier and version" << std::endl; + else + dependencies << QString::fromUtf8(dep[1]) + QLatin1Char(' ') + QString::fromUtf8(dep[2]).simplified(); } } while (line.length() > 0); @@ -202,12 +173,7 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { pluginInfo[pluginsLiteral()] = plugins.simplified(); pluginInfo[classnamesLiteral()] = classnames.simplified(); if (dependencies.length()) - dependencies.removeDuplicates(); pluginInfo[dependenciesLiteral()] = dependencies; - if (!componentFiles.isEmpty()) - pluginInfo[componentsLiteral()] = componentFiles; - if (!scriptFiles.isEmpty()) - pluginInfo[scriptsLiteral()] = scriptFiles; return pluginInfo; } @@ -281,8 +247,6 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) QVariantMap plugininfo = pluginsForModulePath(import.value(pathLiteral()).toString()); QString plugins = plugininfo.value(pluginsLiteral()).toString(); QString classnames = plugininfo.value(classnamesLiteral()).toString(); - QStringList components = plugininfo.value(componentsLiteral()).toStringList(); - QStringList scripts = plugininfo.value(scriptsLiteral()).toStringList(); if (!plugins.isEmpty()) import.insert(QStringLiteral("plugin"), plugins); if (!classnames.isEmpty()) @@ -298,12 +262,6 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) importsCopy.append(depImport); } } - if (!components.isEmpty()) { - import.insert(componentsLiteral(), components); - } - if (!scripts.isEmpty()) { - import.insert(scriptsLiteral(), scripts); - } } done.append(import); } |