From 6420ad91d30e0cc3c2b7500d7c4b4bf1fce7c771 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Wed, 15 Jan 2020 09:56:10 +0100 Subject: Fix QAccessibleQuickWindow::focusChild() to return focused descendant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same as the focusChild fix for qtbase: a132e02540 Fix QAccessibleWidget::focusChild() to return focused descendant Task-number: QTBUG-78284 Change-Id: Ibc3e3287790ebc879513a5b1a739e3a919e1f038 Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Jan Arve Sæther --- src/quick/accessible/qaccessiblequickview.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/quick/accessible/qaccessiblequickview.cpp b/src/quick/accessible/qaccessiblequickview.cpp index 41a02fc09c..b23b0316f5 100644 --- a/src/quick/accessible/qaccessiblequickview.cpp +++ b/src/quick/accessible/qaccessiblequickview.cpp @@ -84,8 +84,12 @@ QAccessibleInterface *QAccessibleQuickWindow::child(int index) const QAccessibleInterface *QAccessibleQuickWindow::focusChild() const { QObject *focusObject = window()->focusObject(); - if (focusObject) - return QAccessible::queryAccessibleInterface(focusObject); + if (focusObject) { + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(focusObject); + if (!iface || iface == this || !iface->focusChild()) + return iface; + return iface->focusChild(); + } return nullptr; } -- cgit v1.2.3 From 19cc92d170eaba83812be2abda15348ecfa2f072 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 7 Feb 2020 09:57:02 +0100 Subject: QV4Engine: Do not construct invalid QVariant If the provided typeHint is -1, it does not make sense to construct a QVariant of this type and to check whether it is appendable. Fixes: QTBUG-81945 Change-Id: I32cbb9e70e210a7eca8d55801c1783338d1173b7 Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4engine.cpp | 40 +++++++++++++++++--------------- tests/auto/qml/qjsvalue/tst_qjsvalue.cpp | 7 +++++- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 81f55673cd..d885c28166 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1515,27 +1515,29 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (succeeded) return retn; #endif - retn = QVariant(typeHint, QMetaType::create(typeHint)); - auto retnAsIterable = retn.value(); - if (retnAsIterable._iteratorCapabilities & QtMetaTypePrivate::ContainerIsAppendable) { - auto const length = a->getLength(); - QV4::ScopedValue arrayValue(scope); - for (qint64 i = 0; i < length; ++i) { - arrayValue = a->get(i); - QVariant asVariant = toVariant(e, arrayValue, retnAsIterable._metaType_id, false, visitedObjects); - auto originalType = asVariant.userType(); - bool couldConvert = asVariant.convert(retnAsIterable._metaType_id); - if (!couldConvert) { - qWarning() << QLatin1String("Could not convert array value at position %1 from %2 to %3") - .arg(QString::number(i), - QMetaType::typeName(originalType), - QMetaType::typeName(retnAsIterable._metaType_id)); - // create default constructed value - asVariant = QVariant(retnAsIterable._metaType_id, nullptr); + if (typeHint != -1) { + retn = QVariant(typeHint, QMetaType::create(typeHint)); + auto retnAsIterable = retn.value(); + if (retnAsIterable._iteratorCapabilities & QtMetaTypePrivate::ContainerIsAppendable) { + auto const length = a->getLength(); + QV4::ScopedValue arrayValue(scope); + for (qint64 i = 0; i < length; ++i) { + arrayValue = a->get(i); + QVariant asVariant = toVariant(e, arrayValue, retnAsIterable._metaType_id, false, visitedObjects); + auto originalType = asVariant.userType(); + bool couldConvert = asVariant.convert(retnAsIterable._metaType_id); + if (!couldConvert) { + qWarning() << QLatin1String("Could not convert array value at position %1 from %2 to %3") + .arg(QString::number(i), + QMetaType::typeName(originalType), + QMetaType::typeName(retnAsIterable._metaType_id)); + // create default constructed value + asVariant = QVariant(retnAsIterable._metaType_id, nullptr); + } + retnAsIterable.append(asVariant.constData()); } - retnAsIterable.append(asVariant.constData()); + return retn; } - return retn; } } diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 95f554776f..cab4686c37 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -1128,6 +1128,10 @@ void tst_QJSValue::toVariant() // array { + auto handler = qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &, const QString &) { + if (type == QtMsgType::QtWarningMsg) + QFAIL("Converting QJSValue to QVariant should not cause error messages"); + }); QVariantList listIn; listIn << 123 << "hello"; QJSValue array = eng.toScriptValue(listIn); @@ -1145,8 +1149,9 @@ void tst_QJSValue::toVariant() QCOMPARE(array2.property("length").toInt(), array.property("length").toInt()); for (int i = 0; i < array.property("length").toInt(); ++i) QVERIFY(array2.property(i).strictlyEquals(array.property(i))); - } + qInstallMessageHandler(handler); + } } void tst_QJSValue::toQObject_nonQObject_data() -- cgit v1.2.3 From 01128c02c040373359ad45b64ccde11dc42c59f6 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Thu, 9 Jan 2020 15:04:24 +0100 Subject: Android: Don't include QtQuickParticles in the dependencies Before qmlimportscanner it was necessary to mark QtQuickParticles as a dependency, but now it is possible to have qmlimportscanner and androiddeployqt handle all of this for us. So we no longer need to make it an explicit dependency. Task-number: QTBUG-38296 Change-Id: I6e0baefdcf002746af26d8c95fa1f318770cc658 Reviewed-by: Assam Boudjelthia Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/quick.pro | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/quick/quick.pro b/src/quick/quick.pro index 700f794af4..cd8ee35135 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -18,13 +18,10 @@ exists("qqml_enable_gcov") { QMAKE_DOCS = $$PWD/doc/qtquick.qdocconf -ANDROID_LIB_DEPENDENCIES = \ - lib/libQt5QuickParticles.so MODULE_PLUGIN_TYPES += \ scenegraph ANDROID_BUNDLED_FILES += \ - qml \ - lib/libQt5QuickParticles.so + qml include(util/util.pri) include(scenegraph/scenegraph.pri) -- cgit v1.2.3 From 897fd7f88b618484ed984b71933b02f40f072a5a Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 11 Feb 2020 06:29:13 +0100 Subject: Mention styleName in the Font QML type documentation Change-Id: I1f2ec7b4bb96955f57b3afd4a295f04dbb70987f Reviewed-by: Paul Wicking --- src/quick/doc/src/qmltypereference.qdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 418475f100..528444cad3 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -179,6 +179,7 @@ available when you import \c QtQuick. \li \l bool \c font.kerning \li \l bool \c font.preferShaping \li \l enumeration \c font.hintingPreference + \li \l string \c font.styleName \endlist Example: -- cgit v1.2.3 From 2e98ccde7b89bdb45c87c154d27566215533ac76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= Date: Mon, 10 Feb 2020 13:49:43 +0100 Subject: Remove /get from CMAKE_QML_DIR Task-number: QTBUG-80843 Change-Id: I3a871930c9920159f455a1d4e8ef7c20e7966b52 Reviewed-by: Joerg Bornemann Reviewed-by: Alexandru Croitor --- tools/qmlimportscanner/qmlimportscanner.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/qmlimportscanner/qmlimportscanner.pro b/tools/qmlimportscanner/qmlimportscanner.pro index d69f1a3b0b..33089a5c48 100644 --- a/tools/qmlimportscanner/qmlimportscanner.pro +++ b/tools/qmlimportscanner/qmlimportscanner.pro @@ -14,9 +14,9 @@ contains(CMAKE_BIN_DIR, "^\\.\\./.*") { CMAKE_BIN_DIR_IS_ABSOLUTE = True } -CMAKE_QML_DIR = $$cmakeRelativePath($$[QT_INSTALL_QML/get], $$[QT_INSTALL_PREFIX]) +CMAKE_QML_DIR = $$cmakeRelativePath($$[QT_INSTALL_QML], $$[QT_INSTALL_PREFIX]) contains(CMAKE_QML_DIR, "^\\.\\./.*") { - CMAKE_QML_DIR = $$[QT_INSTALL_QML/get]/ + CMAKE_QML_DIR = $$[QT_INSTALL_QML]/ CMAKE_QML_DIR_IS_ABSOLUTE = True } load(qt_build_paths) -- cgit v1.2.3 From 2f4c131805f4009f42abe991e0f92f096bbc55fd Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 11 Feb 2020 11:21:03 +0100 Subject: Fix QQmlProperty and Connections for properties starting with '_' We do a weird renaming for the change handler of properties starting with '_', now we do it at least in a consistent way. Fixes: QTBUG-82017 Change-Id: I1535a5ee462f3a344c972461f1fb954f039aa854 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlproperty.cpp | 9 +++++++-- src/qml/types/qqmlconnections.cpp | 3 ++- tests/auto/qml/qqmlconnections/data/underscore.qml | 14 ++++++++++++++ .../auto/qml/qqmlconnections/tst_qqmlconnections.cpp | 15 +++++++++++++++ tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 20 ++++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qqmlconnections/data/underscore.qml diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index d191d07258..86cb606f83 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -353,10 +353,15 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) if (terminal.count() >= 3 && terminal.at(0) == QLatin1Char('o') && terminal.at(1) == QLatin1Char('n') && - terminal.at(2).isUpper()) { + (terminal.at(2).isUpper() || terminal.at(2) == '_')) { QString signalName = terminal.mid(2).toString(); - signalName[0] = signalName.at(0).toLower(); + int firstNon_; + int length = signalName.length(); + for (firstNon_ = 0; firstNon_ < length; ++firstNon_) + if (signalName.at(firstNon_) != '_') + break; + signalName[firstNon_] = signalName.at(firstNon_).toLower(); // XXX - this code treats methods as signals diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 1e801641e5..4c44bba43e 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -238,7 +238,8 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointerstringAt(binding->propertyNameIndex); - if (!propName.startsWith(QLatin1String("on")) || (propName.length() < 3 || !propName.at(2).isUpper())) { + const bool thirdCharacterIsValid = (propName.length() >= 2) && (propName.at(2).isUpper() || propName.at(2) == '_'); + if (!propName.startsWith(QLatin1String("on")) || !thirdCharacterIsValid) { error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); return; } diff --git a/tests/auto/qml/qqmlconnections/data/underscore.qml b/tests/auto/qml/qqmlconnections/data/underscore.qml new file mode 100644 index 0000000000..0f73dc8f17 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/underscore.qml @@ -0,0 +1,14 @@ +import QtQuick 2.12 + +Item { + id: item + property bool success: false + property bool sanityCheck: false + property int __underscore_property: 0 + on__Underscore_propertyChanged: item.sanityCheck = true + + Connections { + target: item + on__Underscore_propertyChanged: item.success = true + } +} diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp index 07af519a3d..f144002875 100644 --- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp +++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp @@ -77,6 +77,8 @@ private slots: void noAcceleratedGlobalLookup_data() { prefixes(); } void noAcceleratedGlobalLookup(); + void bindToPropertyWithUnderscoreChangeHandler(); + private: QQmlEngine engine; void prefixes(); @@ -474,6 +476,19 @@ void tst_qqmlconnections::noAcceleratedGlobalLookup() QCOMPARE(val.toInt(), int(Proxy::EnumValue)); } +void tst_qqmlconnections::bindToPropertyWithUnderscoreChangeHandler() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("underscore.qml")); + QScopedPointer root {component.create()}; + QVERIFY(root); + QQmlProperty underscoreProperty(root.get(), "__underscore_property"); + QVERIFY(underscoreProperty.isValid()); + underscoreProperty.write(42); + QVERIFY(root->property("sanityCheck").toBool()); + QVERIFY(root->property("success").toBool()); +} + QTEST_MAIN(tst_qqmlconnections) #include "tst_qqmlconnections.moc" diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index a15c00ad62..864a47e998 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -158,6 +158,8 @@ private slots: void copy(); void nestedQQmlPropertyMap(); + + void underscorePropertyChangeHandler(); private: QQmlEngine engine; }; @@ -2230,6 +2232,24 @@ void tst_qqmlproperty::nestedQQmlPropertyMap() QCOMPARE(success.read().toString(), QLatin1String("success")); } +void tst_qqmlproperty::underscorePropertyChangeHandler() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(R"( + import QtQuick 2.12 + + Item { + property int __withUnderScore + } + )", QUrl::fromLocalFile(".")); + QScopedPointer root { component.create() }; + QVERIFY(root); + QQmlProperty changeHandler(root.get(), "on__WithUnderScoreChanged"); + QVERIFY(changeHandler.isValid()); + QVERIFY(changeHandler.isSignalProperty()); +} + QTEST_MAIN(tst_qqmlproperty) #include "tst_qqmlproperty.moc" -- cgit v1.2.3 From 70120c931faa0c6139354c75fcf8d39b77ec3b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Fri, 22 Nov 2019 17:03:43 +0100 Subject: Fix a bug where a layout could crash or become non-responsive This happened because of that QQuickText is ill-behaving: When the width on a QQuickText with word wrap enabled is changed to something less than its implicitWidth, its implicitHeight is changed to reflect the height it needs in order to fit all lines. This will cause the layout to be invalidated, and rearranged again. However, at the same time it will also change its implicitWidth actually become wider than its initial implicitWidth (this seems to be a bug). So the next time it is rearranged it will actually be wide enough so that it doesn't need to wrap. This again will cause its implicitWidth and implicitHeight to change to reflect that only one line is needed, which again will cause it to rearrange, but since the item has a too small width for that it will again change the implicitHeight to reflect that it needs more lines..... This went on forever until there was a stack overflow. In addition it also caused an endless (that is, if it didn't stack overflow) updatePolish()/polish() loop (basically polish() was called from within updatePolish() continuously). To make the layout more robust for such "ill-behaving" items we have to add one recursion guard, and one polish-loop guard. Change-Id: I9f752ed320a100c8d0f0fd340347a556e63318e5 Task-number: QTBUG-73683 Reviewed-by: Richard Moe Gustavsen --- src/imports/layouts/qquicklayout.cpp | 21 ++++++++++++--- src/imports/layouts/qquicklayout_p.h | 4 ++- src/imports/layouts/qquicklinearlayout.cpp | 10 +++++++ src/imports/layouts/qquicklinearlayout_p.h | 4 ++- .../quick/qquicklayouts/data/tst_rowlayout.qml | 31 ++++++++++++++++++++++ 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp index 450cf26cea..17d37b5c4e 100644 --- a/src/imports/layouts/qquicklayout.cpp +++ b/src/imports/layouts/qquicklayout.cpp @@ -701,8 +701,10 @@ QQuickItem *QQuickLayoutAttached::item() const QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent) - : QQuickItem(dd, parent), - m_dirty(false) + : QQuickItem(dd, parent) + , m_dirty(false) + , m_inUpdatePolish(false) + , m_polishInsideUpdatePolish(0) { } @@ -729,7 +731,9 @@ QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object) void QQuickLayout::updatePolish() { + m_inUpdatePolish = true; rearrange(QSizeF(width(), height())); + m_inUpdatePolish = false; } void QQuickLayout::componentComplete() @@ -750,7 +754,18 @@ void QQuickLayout::invalidate(QQuickItem * /*childItem*/) if (!qobject_cast(parentItem())) { quickLayoutDebug() << "QQuickLayout::invalidate(), polish()"; - polish(); + + if (m_inUpdatePolish) + ++m_polishInsideUpdatePolish; + else + m_polishInsideUpdatePolish = 0; + + if (m_polishInsideUpdatePolish <= 2) + // allow at most two consecutive loops in order to respond to height-for-width + // (e.g QQuickText changes implicitHeight when its width gets changed) + polish(); + else + qWarning() << "Qt Quick Layouts: Polish loop detected. Aborting after two iterations."; } } diff --git a/src/imports/layouts/qquicklayout_p.h b/src/imports/layouts/qquicklayout_p.h index 3f076c7304..d2421667be 100644 --- a/src/imports/layouts/qquicklayout_p.h +++ b/src/imports/layouts/qquicklayout_p.h @@ -119,7 +119,9 @@ protected slots: void invalidateSenderItem(); private: - bool m_dirty; + unsigned m_dirty : 1; + unsigned m_inUpdatePolish : 1; + unsigned m_polishInsideUpdatePolish : 2; Q_DECLARE_PRIVATE(QQuickLayout) diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp index c8d2ac5eb2..911793d748 100644 --- a/src/imports/layouts/qquicklinearlayout.cpp +++ b/src/imports/layouts/qquicklinearlayout.cpp @@ -469,6 +469,16 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size) if (!isReady()) return; + const auto refCounter = qScopeGuard([&d] { + --(d->m_recurRearrangeCounter); + }); + if (d->m_recurRearrangeCounter++ == 2) { + // allow a recursive depth of two in order to respond to height-for-width + // (e.g QQuickText changes implicitHeight when its width gets changed) + qWarning() << "Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations."; + return; + } + d->m_rearranging = true; quickLayoutDebug() << objectName() << "QQuickGridLayoutBase::rearrange()" << size; Qt::LayoutDirection visualDir = effectiveLayoutDirection(); diff --git a/src/imports/layouts/qquicklinearlayout_p.h b/src/imports/layouts/qquicklinearlayout_p.h index 6706ebf9fa..866d0884a2 100644 --- a/src/imports/layouts/qquicklinearlayout_p.h +++ b/src/imports/layouts/qquicklinearlayout_p.h @@ -104,7 +104,8 @@ class QQuickGridLayoutBasePrivate : public QQuickLayoutPrivate Q_DECLARE_PUBLIC(QQuickGridLayoutBase) public: - QQuickGridLayoutBasePrivate() : m_rearranging(false) + QQuickGridLayoutBasePrivate() : m_recurRearrangeCounter(0) + , m_rearranging(false) , m_updateAfterRearrange(false) , m_layoutDirection(Qt::LeftToRight) {} @@ -117,6 +118,7 @@ public: QQuickGridLayoutEngine engine; Qt::Orientation orientation; + unsigned m_recurRearrangeCounter : 2; unsigned m_rearranging : 1; unsigned m_updateAfterRearrange : 1; QVector m_invalidateAfterRearrange; diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml index 07af6a77ac..0732884c97 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml @@ -1100,5 +1100,36 @@ Item { waitForRendering(rootRect.layout) compare(rootRect.item1.width, 100) } + +//--------------------------- + Component { + id: rowlayoutWithTextItems_Component + RowLayout { + Text { + Layout.fillWidth: true + text: "OneWord" + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + Text { + Layout.fillWidth: true + text: "OneWord" + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + } + } + + // QTBUG-73683 + function test_rowlayoutWithTextItems() { + var layout = createTemporaryObject(rowlayoutWithTextItems_Component, container) + waitForRendering(layout) + for (var i = 0; i < 3; i++) { + ignoreWarning(/Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations./) + } + ignoreWarning(/Qt Quick Layouts: Polish loop detected. Aborting after two iterations./) + layout.width = layout.width - 2 // set the size to be smaller than its "minimum size" + waitForRendering(layout) // do not exit before all warnings have been received + + // DO NOT CRASH due to stack overflow (or loop endlessly due to updatePolish()/polish() loop) + } } } -- cgit v1.2.3 From 1a7ca47a7a92945e16006ddd00adea186535cdd5 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 12 Feb 2020 13:29:08 +0100 Subject: QQuickLoader: store status This allows us to emit the change signal only when the status has actually changed. Due to alignment restrictions, this does not even change the size of the class. Fixes: QTBUG-82002 Change-Id: I2192bf80e72b92108046780811c3acdab39af518 Reviewed-by: Ulf Hermann --- src/quick/items/qquickloader.cpp | 93 +++++++++++++--------- src/quick/items/qquickloader_p_p.h | 7 ++ .../auto/quick/qquickloader/data/statusChanged.qml | 16 ++++ tests/auto/quick/qquickloader/tst_qquickloader.cpp | 14 ++++ 4 files changed, 91 insertions(+), 39 deletions(-) create mode 100644 tests/auto/quick/qquickloader/data/statusChanged.qml diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index b389e7a11b..8c15b60712 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -55,7 +55,7 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges QQuickLoaderPrivate::QQuickLoaderPrivate() : item(nullptr), object(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false), - active(true), loadingFromSource(false), asynchronous(false) + active(true), loadingFromSource(false), asynchronous(false), status(computeStatus()) { } @@ -378,7 +378,7 @@ void QQuickLoader::setActive(bool newVal) d->object = nullptr; emit itemChanged(); } - emit statusChanged(); + d->updateStatus(); } emit activeChanged(); } @@ -431,7 +431,7 @@ void QQuickLoader::loadFromSource() Q_D(QQuickLoader); if (d->source.isEmpty()) { emit sourceChanged(); - emit statusChanged(); + d->updateStatus(); emit progressChanged(); emit itemChanged(); return; @@ -502,7 +502,7 @@ void QQuickLoader::loadFromSourceComponent() Q_D(QQuickLoader); if (!d->component) { emit sourceComponentChanged(); - emit statusChanged(); + d->updateStatus(); emit progressChanged(); emit itemChanged(); return; @@ -618,7 +618,7 @@ void QQuickLoaderPrivate::load() q, SLOT(_q_sourceLoaded())); QObject::connect(component, SIGNAL(progressChanged(qreal)), q, SIGNAL(progressChanged())); - emit q->statusChanged(); + updateStatus(); emit q->progressChanged(); if (loadingFromSource) emit q->sourceChanged(); @@ -705,7 +705,7 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status) emit q->sourceChanged(); else emit q->sourceComponentChanged(); - emit q->statusChanged(); + updateStatus(); emit q->progressChanged(); if (status == QQmlIncubator::Ready) emit q->loaded(); @@ -722,7 +722,7 @@ void QQuickLoaderPrivate::_q_sourceLoaded() emit q->sourceChanged(); else emit q->sourceComponentChanged(); - emit q->statusChanged(); + updateStatus(); emit q->progressChanged(); emit q->itemChanged(); //Like clearing source, emit itemChanged even if previous item was also null disposeInitialPropertyValues(); // cleanup @@ -740,7 +740,7 @@ void QQuickLoaderPrivate::_q_sourceLoaded() component->create(*incubator, itemContext); if (incubator && incubator->status() == QQmlIncubator::Loading) - emit q->statusChanged(); + updateStatus(); } /*! @@ -787,37 +787,7 @@ QQuickLoader::Status QQuickLoader::status() const { Q_D(const QQuickLoader); - if (!d->active) - return Null; - - if (d->component) { - switch (d->component->status()) { - case QQmlComponent::Loading: - return Loading; - case QQmlComponent::Error: - return Error; - case QQmlComponent::Null: - return Null; - default: - break; - } - } - - if (d->incubator) { - switch (d->incubator->status()) { - case QQmlIncubator::Loading: - return Loading; - case QQmlIncubator::Error: - return Error; - default: - break; - } - } - - if (d->object) - return Ready; - - return d->source.isEmpty() ? Null : Error; + return static_cast(d->status); } void QQuickLoader::componentComplete() @@ -1018,6 +988,51 @@ QV4::ReturnedValue QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV4Funct return valuemap->asReturnedValue(); } +QQuickLoader::Status QQuickLoaderPrivate::computeStatus() const +{ + if (!active) + return QQuickLoader::Status::Null; + + if (component) { + switch (component->status()) { + case QQmlComponent::Loading: + return QQuickLoader::Status::Loading; + case QQmlComponent::Error: + return QQuickLoader::Status::Error; + case QQmlComponent::Null: + return QQuickLoader::Status::Null; + default: + break; + } + } + + if (incubator) { + switch (incubator->status()) { + case QQmlIncubator::Loading: + return QQuickLoader::Status::Loading; + case QQmlIncubator::Error: + return QQuickLoader::Status::Error; + default: + break; + } + } + + if (object) + return QQuickLoader::Status::Ready; + + return source.isEmpty() ? QQuickLoader::Status::Null : QQuickLoader::Status::Error; +} + +void QQuickLoaderPrivate::updateStatus() +{ + Q_Q(QQuickLoader); + auto newStatus = computeStatus(); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + #include QT_END_NAMESPACE diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index 349b5c6c06..39d50280c5 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -96,6 +96,8 @@ public: void disposeInitialPropertyValues(); static QUrl resolveSourceUrl(QQmlV4Function *args); QV4::ReturnedValue extractInitialPropertyValues(QQmlV4Function *args, QObject *loader, bool *error); + QQuickLoader::Status computeStatus() const; + void updateStatus(); qreal getImplicitWidth() const override; qreal getImplicitHeight() const override; @@ -112,6 +114,11 @@ public: bool active : 1; bool loadingFromSource : 1; bool asynchronous : 1; + // We need to use char instead of QQuickLoader::Status + // as otherwise the size of the class would increase + // on 32-bit systems, as sizeof(Status) == sizeof(int) + // and sizeof(int) > remaining padding on 32 bit + char status; void _q_sourceLoaded(); void _q_updateSize(bool loaderGeometryChanged = true); diff --git a/tests/auto/quick/qquickloader/data/statusChanged.qml b/tests/auto/quick/qquickloader/data/statusChanged.qml new file mode 100644 index 0000000000..fe46bc7b24 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/statusChanged.qml @@ -0,0 +1,16 @@ +import QtQuick 2.12 +import QtQuick.Window 2.12 + +Window { + id: root + property int statusChangedCounter: 0 + property alias status: loader.status + visible: true; width: 640; height: 480 + Loader { + id: loader + anchors.fill: parent + asynchronous: true + source: "./RedRect.qml" + onStatusChanged: root.statusChangedCounter++ + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index d0a1a8a45b..7bd6f9466a 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -39,6 +39,7 @@ #include "testhttpserver.h" #include "../../shared/util.h" #include "../shared/geometrytestutil.h" +#include Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") @@ -128,6 +129,7 @@ private slots: void rootContext(); void sourceURLKeepComponent(); + void statusChangeOnlyEmittedOnce(); }; Q_DECLARE_METATYPE(QList) @@ -1446,6 +1448,18 @@ void tst_QQuickLoader::sourceURLKeepComponent() } +// QTBUG-82002 +void tst_QQuickLoader::statusChangeOnlyEmittedOnce() +{ + QQmlApplicationEngine engine; + auto url = testFileUrl("statusChanged.qml"); + engine.load(url); + auto root = engine.rootObjects().at(0); + QVERIFY(root); + QTRY_COMPARE(QQuickLoader::Status(root->property("status").toInt()), QQuickLoader::Ready); + QCOMPARE(root->property("statusChangedCounter").toInt(), 2); // 1xLoading + 1xReady*/ +} + QTEST_MAIN(tst_QQuickLoader) #include "tst_qquickloader.moc" -- cgit v1.2.3 From ac0ce38dcfeeded87db0c1dd5d348f5ed6fc1af3 Mon Sep 17 00:00:00 2001 From: Anton Kreuzkamp Date: Wed, 12 Feb 2020 12:48:47 +0100 Subject: Add a null check to QQuickWindowPrivate::renderer Afaik there currently is no case in a regular execution where the renderer would actually be null, but for the sake of correctness add a null check as this seems to be the only code location that actually assumes the renderer to be non-null. Additionally, in order to en-/disable custom render modes at runtime, GammaRay actually needs to recreate the renderer and therefore resets the renderer to null in order for QQuickWindowPrivate::syncSceneGraph to recreate it. Thus we need a null check to make sure we don't crash in the short time frame where the renderer is null. Change-Id: Ief5c405f3bc8725d55e22cd33f2164830764e33d Reviewed-by: Laszlo Agocs --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index faa22ec83f..f07136fcd3 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -548,7 +548,7 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa else context->endNextFrame(renderer); - if (renderer->hasCustomRenderModeWithContinuousUpdate()) { + if (renderer && renderer->hasCustomRenderModeWithContinuousUpdate()) { // For the overdraw visualizer. This update is not urgent so avoid a // direct update() call, this is only here to keep the overdraw // visualization box rotating even when the scene is static. -- cgit v1.2.3