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
translationsById.qml
generalizedGroupedProperty.qml
appendToQQmlListProperty.qml
# support types:
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);
}
QQmlListProperty<QObject> Extension::getMyList()
{
return QQmlListProperty<QObject>(this, &m_myList);
}

View File

@ -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<QObject> myList READ getMyList)
QProperty<int> m_extCount { 0 };
QProperty<double> m_foo { 0 };
QList<QObject *> m_myList;
public:
Extension(QObject *parent = nullptr);
@ -27,6 +29,8 @@ public:
double getFoo() const;
void setFoo(double v);
QBindable<double> bindableFoo();
QQmlListProperty<QObject> getMyList();
};
class IndirectExtension : public Extension

View File

@ -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<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)

View File

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

View File

@ -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;
}
}