From 60ca8dae85de1bd0df4d549f237534c68d75091c Mon Sep 17 00:00:00 2001 From: Sami Shalayel Date: Fri, 16 Sep 2022 14:38:19 +0200 Subject: [PATCH] qmltc: stop using QQmlListReference to append Instead of using a QQmlListReference to append into lists, use directly the underlying QList for types compiled with qmltc and QQmlListProperty for types not compiled with qmltc (e.g. types defined in c++). Add a test that tests the private c++ backed "data" property of QQuickItem and different kind of lists (QtObjects and HelloWorlds). Also, append multiple objects to QLists in one go. Fixes: QTBUG-104664 Change-Id: Id5ce6307a241c4c8a167feca677ba5e9b492bc07 Reviewed-by: Ulf Hermann --- .../auto/qml/qmltc/QmltcTests/CMakeLists.txt | 1 + .../QmltcTests/appendToQQmlListProperty.qml | 70 ++++++++++++++++++ .../QmltcTests/cpptypes/extensiontypes.cpp | 5 ++ .../QmltcTests/cpptypes/extensiontypes.h | 4 + tests/auto/qml/qmltc/tst_qmltc.cpp | 74 +++++++++++++++++++ tests/auto/qml/qmltc/tst_qmltc.h | 1 + tools/qmltc/qmltccompilerpieces.cpp | 16 ++-- 7 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml diff --git a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt index 5a917f1c5a..4c1cab7bd3 100644 --- a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt +++ b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt @@ -98,6 +98,7 @@ set(qml_sources translations.qml translationsById.qml generalizedGroupedProperty.qml + appendToQQmlListProperty.qml # support types: DefaultPropertySingleChild.qml diff --git a/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml b/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml new file mode 100644 index 0000000000..680e297edb --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/appendToQQmlListProperty.qml @@ -0,0 +1,70 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QmltcTests + +Item { + + property var myComponentList + property list myValueTypeList + property list myQtObjectList + property list myHelloWorldList + + property Item myItem: Item { + // test qquickitems default data list (appending to private property of qquickitem) + HelloWorld { + hello: "hello1" + } + HelloWorld { + hello: "hello2" + } + Rectangle { + property string hello: "I am a Rectangle." + } + } + + myComponentList: [ + "Hello", + 42, + 4.0, + ] + + myValueTypeList: [ + 12489, + 10, + 42 + ] + + myQtObjectList: [ + HelloWorld { + hello: "Guten Morgen!" + }, + Rectangle { + property string hello: "I am a Rectangle." + }, + HelloWorld { + hello: "Moin!" + } + ] + + myHelloWorldList: [ + HelloWorld{ + hello: "Good morning1" + }, + HelloWorld { + hello: "Good morning2" + }, + HelloWorld { + hello: "Good morning3" + } + ] + + property var extended: TypeWithExtension { + myList: [ + HelloWorld {}, + Rectangle {}, + QtObject {} + ] + } +} diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp index 38a096956f..16f555c0b7 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.cpp @@ -100,3 +100,8 @@ QBindable TypeWithExtensionNamespace::bindableCount() { return QBindable(&m_count); } + +QQmlListProperty Extension::getMyList() +{ + return QQmlListProperty(this, &m_myList); +} diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h index 9cb7ca4827..667e2952ce 100644 --- a/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/extensiontypes.h @@ -14,9 +14,11 @@ class Extension : public QObject QML_ANONYMOUS Q_PROPERTY(int count READ getCount WRITE setCount BINDABLE bindableCount) Q_PROPERTY(double foo READ getFoo WRITE setFoo BINDABLE bindableFoo) + Q_PROPERTY(QQmlListProperty myList READ getMyList) QProperty m_extCount { 0 }; QProperty m_foo { 0 }; + QList m_myList; public: Extension(QObject *parent = nullptr); @@ -27,6 +29,8 @@ public: double getFoo() const; void setFoo(double v); QBindable bindableFoo(); + + QQmlListProperty getMyList(); }; class IndirectExtension : public Extension diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp index b863bf6007..e37f999ac5 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.cpp +++ b/tests/auto/qml/qmltc/tst_qmltc.cpp @@ -73,6 +73,7 @@ #include "translationsbyid.h" #include "defaultalias.h" #include "generalizedgroupedproperty.h" +#include "appendtoqqmllistproperty.h" #include "testprivateproperty.h" @@ -183,6 +184,7 @@ void tst_qmltc::initTestCase() QUrl("qrc:/qt/qml/QmltcTests/privatePropertySubclass.qml"), QUrl("qrc:/qt/qml/QmltcTests/calqlatrBits.qml"), QUrl("qrc:/qt/qml/QmltcTests/valueTypeListProperty.qml"), + QUrl("qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"), }; QQmlEngine e; @@ -2506,4 +2508,76 @@ void tst_qmltc::generalizedGroupedProperty() } } +void tst_qmltc::appendToQQmlListProperty() +{ + QQmlEngine e; + PREPEND_NAMESPACE(appendToQQmlListProperty) fromQmltc(&e); + QQmlComponent component(&e, "qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer fromEngine(component.create()); + + auto itemFromEngine = fromEngine->property("myItem").value(); + auto itemFromQmltc = fromQmltc.myItem(); + + QCOMPARE(itemFromEngine->children().size(), 3); + QCOMPARE(itemFromQmltc->children().size(), 3); + + QCOMPARE(itemFromEngine->children().at(0)->property("hello"), u"hello1"_s); + QCOMPARE(itemFromEngine->children().at(1)->property("hello"), u"hello2"_s); + QCOMPARE(itemFromEngine->children().at(2)->property("hello"), u"I am a Rectangle."_s); + + QCOMPARE(itemFromQmltc->children().at(0)->property("hello"), u"hello1"_s); + QCOMPARE(itemFromQmltc->children().at(1)->property("hello"), u"hello2"_s); + QCOMPARE(itemFromQmltc->children().at(2)->property("hello"), u"I am a Rectangle."_s); + + QVariantList referenceComponentList = { QVariant(u"Hello"_s), QVariant(42), QVariant(4.0) }; + QCOMPARE(fromQmltc.myComponentList().toList(), referenceComponentList); + QCOMPARE(fromEngine->property("myComponentList").toList(), referenceComponentList); + + QList referenceValueTypeList = { 12489, 10, 42 }; + QVariantList referenceValueTypeList2 = { 12489, 10, 42 }; + QCOMPARE(fromQmltc.myValueTypeList().toList(), referenceValueTypeList); + QCOMPARE(fromEngine->property("myValueTypeList").toList(), referenceValueTypeList2); + + QQmlListReference qtObjectsFromQmltc(&fromQmltc, "myQtObjectList"); + QVERIFY(qtObjectsFromQmltc.isValid()); + QQmlListReference qtObjectsFromEngine(fromEngine.data(), "myQtObjectList"); + QVERIFY(qtObjectsFromEngine.isValid()); + + QCOMPARE(qtObjectsFromQmltc.size(), 3); + QCOMPARE(qtObjectsFromEngine.size(), 3); + + QCOMPARE(qtObjectsFromQmltc.at(0)->property("hello"), u"Guten Morgen!"_s); + QCOMPARE(qtObjectsFromQmltc.at(1)->property("hello"), u"I am a Rectangle."_s); + QCOMPARE(qtObjectsFromQmltc.at(2)->property("hello"), u"Moin!"_s); + + QCOMPARE(qtObjectsFromEngine.at(0)->property("hello"), u"Guten Morgen!"_s); + QCOMPARE(qtObjectsFromEngine.at(1)->property("hello"), u"I am a Rectangle."_s); + QCOMPARE(qtObjectsFromEngine.at(2)->property("hello"), u"Moin!"_s); + + QQmlListReference qtHWFromQmltc(&fromQmltc, "myHelloWorldList"); + QQmlListReference qtHWFromEngine(fromEngine.data(), "myHelloWorldList"); + + QCOMPARE(qtHWFromQmltc.size(), 3); + QCOMPARE(qtHWFromEngine.size(), 3); + + QCOMPARE(qtHWFromQmltc.at(0)->property("hello"), u"Good morning1"_s); + QCOMPARE(qtHWFromQmltc.at(1)->property("hello"), u"Good morning2"_s); + QCOMPARE(qtHWFromQmltc.at(2)->property("hello"), u"Good morning3"_s); + + QCOMPARE(qtHWFromEngine.at(0)->property("hello"), u"Good morning1"_s); + QCOMPARE(qtHWFromEngine.at(1)->property("hello"), u"Good morning2"_s); + QCOMPARE(qtHWFromEngine.at(2)->property("hello"), u"Good morning3"_s); + + // make sure that extensions are handled correctly, as they require a slightly different code + // path to be generated + QQmlListReference extendedFromQmltc(fromQmltc.extended().value(), "myList"); + QVERIFY(extendedFromQmltc.isValid()); + QQmlListReference extendedFromEngine(fromEngine->property("extended").value(), + "myList"); + QVERIFY(extendedFromEngine.isValid()); + QCOMPARE(extendedFromQmltc.size(), 3); + QCOMPARE(extendedFromEngine.size(), 3); +} + QTEST_MAIN(tst_qmltc) diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h index e8e1eefe91..e7685d0a45 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.h +++ b/tests/auto/qml/qmltc/tst_qmltc.h @@ -85,4 +85,5 @@ private slots: void valueTypeListProperty(); void translations(); void generalizedGroupedProperty(); + void appendToQQmlListProperty(); }; diff --git a/tools/qmltc/qmltccompilerpieces.cpp b/tools/qmltc/qmltccompilerpieces.cpp index eedec2f469..a53bb89796 100644 --- a/tools/qmltc/qmltccompilerpieces.cpp +++ b/tools/qmltc/qmltccompilerpieces.cpp @@ -92,17 +92,21 @@ void QmltcCodeGenerator::generate_assignToListProperty( const bool populateLocalListProperty = qmlListVarName.isEmpty(); if (populateLocalListProperty) { - qmlListVarName = u"listref_%1"_s.arg(p.propertyName()); - *block << u"QQmlListReference %1(%2, %3);"_s.arg( - qmlListVarName, accessor, - QQmlJSUtils::toLiteral(p.propertyName(), u"QByteArrayLiteral")); - *block << QStringLiteral("Q_ASSERT(%1.canAppend());").arg(qmlListVarName); + auto [extensionPrologue, extensionAccessor, extensionEpilogue] = + QmltcCodeGenerator::wrap_extensionType( + type, p, QmltcCodeGenerator::wrap_privateClass(accessor, p)); + + qmlListVarName = u"listprop_%1"_s.arg(p.propertyName()); + *block << u"QQmlListProperty<%1> %2;"_s.arg(p.type()->internalName(), qmlListVarName); + *block << extensionPrologue; + *block << u"%1 = %2->%3();"_s.arg(qmlListVarName, extensionAccessor, p.read()); + *block << extensionEpilogue; } for (const QString &value : values) { auto [prologue, wrappedValue, epilogue] = QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value); *block << prologue; - *block << u"%1.append(%2);"_s.arg(qmlListVarName, wrappedValue); + *block << u"%1.append(std::addressof(%1), %2);"_s.arg(qmlListVarName, wrappedValue); *block << epilogue; } }