qmltc: stop using QQmlListReference to append

Instead of using a QQmlListReference to append into lists, use directly
the underlying QList<T*> for types compiled with qmltc and
QQmlListProperty<T> 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 <ulf.hermann@qt.io>
This commit is contained in:
Sami Shalayel 2022-09-16 14:38:19 +02:00
parent 4b7bd4dae3
commit 60ca8dae85
7 changed files with 165 additions and 6 deletions

View File

@ -98,6 +98,7 @@ set(qml_sources
translations.qml translations.qml
translationsById.qml translationsById.qml
generalizedGroupedProperty.qml generalizedGroupedProperty.qml
appendToQQmlListProperty.qml
# support types: # support types:
DefaultPropertySingleChild.qml DefaultPropertySingleChild.qml

View File

@ -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<int> myValueTypeList
property list<QtObject> myQtObjectList
property list<HelloWorld> 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 {}
]
}
}

View File

@ -100,3 +100,8 @@ QBindable<int> TypeWithExtensionNamespace::bindableCount()
{ {
return QBindable<int>(&m_count); return QBindable<int>(&m_count);
} }
QQmlListProperty<QObject> Extension::getMyList()
{
return QQmlListProperty<QObject>(this, &m_myList);
}

View File

@ -14,9 +14,11 @@ class Extension : public QObject
QML_ANONYMOUS QML_ANONYMOUS
Q_PROPERTY(int count READ getCount WRITE setCount BINDABLE bindableCount) Q_PROPERTY(int count READ getCount WRITE setCount BINDABLE bindableCount)
Q_PROPERTY(double foo READ getFoo WRITE setFoo BINDABLE bindableFoo) Q_PROPERTY(double foo READ getFoo WRITE setFoo BINDABLE bindableFoo)
Q_PROPERTY(QQmlListProperty<QObject> myList READ getMyList)
QProperty<int> m_extCount { 0 }; QProperty<int> m_extCount { 0 };
QProperty<double> m_foo { 0 }; QProperty<double> m_foo { 0 };
QList<QObject *> m_myList;
public: public:
Extension(QObject *parent = nullptr); Extension(QObject *parent = nullptr);
@ -27,6 +29,8 @@ public:
double getFoo() const; double getFoo() const;
void setFoo(double v); void setFoo(double v);
QBindable<double> bindableFoo(); QBindable<double> bindableFoo();
QQmlListProperty<QObject> getMyList();
}; };
class IndirectExtension : public Extension class IndirectExtension : public Extension

View File

@ -73,6 +73,7 @@
#include "translationsbyid.h" #include "translationsbyid.h"
#include "defaultalias.h" #include "defaultalias.h"
#include "generalizedgroupedproperty.h" #include "generalizedgroupedproperty.h"
#include "appendtoqqmllistproperty.h"
#include "testprivateproperty.h" #include "testprivateproperty.h"
@ -183,6 +184,7 @@ void tst_qmltc::initTestCase()
QUrl("qrc:/qt/qml/QmltcTests/privatePropertySubclass.qml"), QUrl("qrc:/qt/qml/QmltcTests/privatePropertySubclass.qml"),
QUrl("qrc:/qt/qml/QmltcTests/calqlatrBits.qml"), QUrl("qrc:/qt/qml/QmltcTests/calqlatrBits.qml"),
QUrl("qrc:/qt/qml/QmltcTests/valueTypeListProperty.qml"), QUrl("qrc:/qt/qml/QmltcTests/valueTypeListProperty.qml"),
QUrl("qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"),
}; };
QQmlEngine e; 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<QObject> fromEngine(component.create());
auto itemFromEngine = fromEngine->property("myItem").value<QQuickItem *>();
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<int> 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<QObject *>(), "myList");
QVERIFY(extendedFromQmltc.isValid());
QQmlListReference extendedFromEngine(fromEngine->property("extended").value<QObject *>(),
"myList");
QVERIFY(extendedFromEngine.isValid());
QCOMPARE(extendedFromQmltc.size(), 3);
QCOMPARE(extendedFromEngine.size(), 3);
}
QTEST_MAIN(tst_qmltc) QTEST_MAIN(tst_qmltc)

View File

@ -85,4 +85,5 @@ private slots:
void valueTypeListProperty(); void valueTypeListProperty();
void translations(); void translations();
void generalizedGroupedProperty(); void generalizedGroupedProperty();
void appendToQQmlListProperty();
}; };

View File

@ -92,17 +92,21 @@ void QmltcCodeGenerator::generate_assignToListProperty(
const bool populateLocalListProperty = qmlListVarName.isEmpty(); const bool populateLocalListProperty = qmlListVarName.isEmpty();
if (populateLocalListProperty) { if (populateLocalListProperty) {
qmlListVarName = u"listref_%1"_s.arg(p.propertyName()); auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
*block << u"QQmlListReference %1(%2, %3);"_s.arg( QmltcCodeGenerator::wrap_extensionType(
qmlListVarName, accessor, type, p, QmltcCodeGenerator::wrap_privateClass(accessor, p));
QQmlJSUtils::toLiteral(p.propertyName(), u"QByteArrayLiteral"));
*block << QStringLiteral("Q_ASSERT(%1.canAppend());").arg(qmlListVarName); 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) { for (const QString &value : values) {
auto [prologue, wrappedValue, epilogue] = auto [prologue, wrappedValue, epilogue] =
QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value); QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value);
*block << prologue; *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; *block << epilogue;
} }
} }