ListModel: Treat QV4::Sequence and QV4::QmlListWrapper like arrays
Pick-to: 6.7 Fixes: QTBUG-124084 Change-Id: I99f900b71d71f33f807f5f69829bb9ef760b40c8 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
This commit is contained in:
parent
57c6168d00
commit
07cfd1b9eb
|
@ -3,22 +3,25 @@
|
|||
|
||||
#include "qqmllistmodel_p_p.h"
|
||||
#include "qqmllistmodelworkeragent_p.h"
|
||||
#include <private/qqmlopenmetaobject_p.h>
|
||||
#include <private/qqmljsast_p.h>
|
||||
#include <private/qqmljsengine_p.h>
|
||||
|
||||
#include <private/qjsvalue_p.h>
|
||||
|
||||
#include <private/qqmlcustomparser_p.h>
|
||||
#include <private/qqmlengine_p.h>
|
||||
#include <private/qqmljsast_p.h>
|
||||
#include <private/qqmljsengine_p.h>
|
||||
#include <private/qqmllistwrapper_p.h>
|
||||
#include <private/qqmlnotifier_p.h>
|
||||
#include <private/qqmlopenmetaobject_p.h>
|
||||
|
||||
#include <private/qv4object_p.h>
|
||||
#include <private/qv4dateobject_p.h>
|
||||
#include <private/qv4urlobject_p.h>
|
||||
#include <private/qv4objectiterator_p.h>
|
||||
#include <private/qv4alloca_p.h>
|
||||
#include <private/qv4dateobject_p.h>
|
||||
#include <private/qv4lookup_p.h>
|
||||
#include <private/qv4object_p.h>
|
||||
#include <private/qv4objectiterator_p.h>
|
||||
#include <private/qv4qmlcontext_p.h>
|
||||
#include <private/qv4sequenceobject_p.h>
|
||||
#include <private/qv4urlobject_p.h>
|
||||
|
||||
#include <qqmlcontext.h>
|
||||
#include <qqmlinfo.h>
|
||||
|
@ -681,18 +684,11 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement
|
|||
e->setDoublePropertyFast(r, propertyValue->asDouble());
|
||||
}
|
||||
} else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
|
||||
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
|
||||
if (r.type == ListLayout::Role::List) {
|
||||
ListModel *subModel = new ListModel(r.subLayout, nullptr);
|
||||
|
||||
int arrayLength = a->getLength();
|
||||
for (int j=0 ; j < arrayLength ; ++j) {
|
||||
o = a->get(j);
|
||||
subModel->append(o);
|
||||
}
|
||||
|
||||
e->setListPropertyFast(r, subModel);
|
||||
}
|
||||
setArrayLike(&o, propertyName, e, a);
|
||||
} else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
|
||||
setArrayLike(&o, propertyName, e, s);
|
||||
} else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
|
||||
setArrayLike(&o, propertyName, e, l);
|
||||
} else if (propertyValue->isBoolean()) {
|
||||
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
|
||||
if (r.type == ListLayout::Role::Bool) {
|
||||
|
|
|
@ -391,6 +391,23 @@ private:
|
|||
|
||||
void updateCacheIndices(int start = 0, int end = -1);
|
||||
|
||||
template<typename ArrayLike>
|
||||
void setArrayLike(QV4::ScopedObject *o, QV4::String *propertyName, ListElement *e, ArrayLike *a)
|
||||
{
|
||||
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
|
||||
if (r.type == ListLayout::Role::List) {
|
||||
ListModel *subModel = new ListModel(r.subLayout, nullptr);
|
||||
|
||||
int arrayLength = a->getLength();
|
||||
for (int j=0 ; j < arrayLength ; ++j) {
|
||||
*o = a->get(j);
|
||||
subModel->append(*o);
|
||||
}
|
||||
|
||||
e->setListPropertyFast(r, subModel);
|
||||
}
|
||||
}
|
||||
|
||||
friend class ListElement;
|
||||
friend class QQmlListModelWorkerAgent;
|
||||
friend class QQmlListModelParser;
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: mainWindow
|
||||
|
||||
function load(data) {
|
||||
model.clear()
|
||||
for (var i = 0; i < data.length; i++)
|
||||
model.append(data[i])
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: model
|
||||
}
|
||||
|
||||
Repeater {
|
||||
objectName: "topLevel"
|
||||
model: model
|
||||
Item {
|
||||
objectName: _headline
|
||||
Repeater {
|
||||
objectName: "month"
|
||||
model: _weeks
|
||||
Item {
|
||||
objectName: index
|
||||
Repeater {
|
||||
objectName: "week"
|
||||
model: _week
|
||||
Item {
|
||||
objectName: _day
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include <QtQuick/private/qquicktext_p.h>
|
||||
#include <QtQuick/private/qquickanimation_p.h>
|
||||
#include <QtQuick/private/qquicklistview_p.h>
|
||||
#include <QtQuick/private/qquickrepeater_p.h>
|
||||
#include <QtQml/private/qqmlengine_p.h>
|
||||
#include <QtQmlModels/private/qqmllistmodel_p.h>
|
||||
#include <QtQml/private/qqmlexpression_p.h>
|
||||
|
@ -120,6 +121,7 @@ private slots:
|
|||
void objectOwnershipFlip();
|
||||
void enumsInListElement();
|
||||
void protectQObjectFromGC();
|
||||
void nestedLists();
|
||||
};
|
||||
|
||||
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
|
||||
|
@ -1906,6 +1908,154 @@ void tst_qqmllistmodel::protectQObjectFromGC()
|
|||
}
|
||||
}
|
||||
|
||||
static QVariantList createLast7Days()
|
||||
{
|
||||
QVariantList last7DaysList;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
QVariantMap map;
|
||||
map.insert("_day", i);
|
||||
last7DaysList.append(map);
|
||||
}
|
||||
return last7DaysList;
|
||||
}
|
||||
|
||||
static QVariantList createWeekChartModels()
|
||||
{
|
||||
QVariantList list;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
QVariantMap map;
|
||||
map.insert("_week", createLast7Days());
|
||||
list.append(map);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static QVariantList createVariantModel()
|
||||
{
|
||||
QVariantMap element1;
|
||||
element1.insert("_headline", "Element 1");
|
||||
element1.insert("_weeks", createWeekChartModels());
|
||||
|
||||
QVariantMap element2;
|
||||
element2.insert("_headline", "Element 2");
|
||||
element2.insert("_weeks", createWeekChartModels());
|
||||
|
||||
QVariantMap element3;
|
||||
element3.insert("_headline", "Element 3");
|
||||
element3.insert("_weeks", createWeekChartModels());
|
||||
|
||||
QVariantList list;
|
||||
list.append(element1);
|
||||
list.append(element2);
|
||||
list.append(element3);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
class Day : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int _day READ _day CONSTANT)
|
||||
public:
|
||||
Day(int day, QObject *parent = nullptr) : QObject(parent), day(day) {}
|
||||
int _day() const { return day; }
|
||||
private:
|
||||
int day = 0;
|
||||
};
|
||||
|
||||
class Week : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQmlListProperty<Day> _week READ _week)
|
||||
public:
|
||||
Week(QObject *parent = nullptr) : QObject(parent)
|
||||
{
|
||||
for (int i = 0; i < 7; ++i)
|
||||
week.append(new Day(i, this));
|
||||
}
|
||||
|
||||
QQmlListProperty<Day> _week() { return QQmlListProperty<Day>(this, &week); }
|
||||
|
||||
private:
|
||||
QList<Day *> week;
|
||||
};
|
||||
|
||||
class Month : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQmlListProperty<Week> _weeks READ _weeks)
|
||||
Q_PROPERTY(QString _headline READ _headline CONSTANT)
|
||||
public:
|
||||
|
||||
Month(int i, QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
, headline(QLatin1String("Element ") + QString::number(i))
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
weeks.append(new Week(this));
|
||||
}
|
||||
|
||||
QQmlListProperty<Week> _weeks() { return QQmlListProperty<Week>(this, &weeks); }
|
||||
QString _headline() const { return headline; }
|
||||
|
||||
private:
|
||||
QList<Week *> weeks;
|
||||
QString headline;
|
||||
};
|
||||
|
||||
static void verifyLists(const QVariantList &list, QQuickRepeater *topLevel)
|
||||
{
|
||||
QVERIFY(topLevel);
|
||||
QCOMPARE(topLevel->count(), 3);
|
||||
|
||||
for (int month = 0; month < 3; ++month) {
|
||||
const QVariantMap monthData = list[month].toMap();
|
||||
const QQuickItem *monthItem = topLevel->itemAt(month);
|
||||
QCOMPARE(monthItem->objectName(), monthData["_headline"].toString());
|
||||
const QQuickRepeater *monthRepeater = monthItem->findChild<QQuickRepeater *>("month");
|
||||
QVERIFY(monthRepeater);
|
||||
QCOMPARE(monthRepeater->count(), 4);
|
||||
const QVariantList weekList = monthData["_weeks"].toList();
|
||||
for (int week = 0; week < 4; ++week) {
|
||||
const QVariantList weekData = weekList[week].toMap()["_week"].toList();
|
||||
const QQuickItem *weekItem = monthRepeater->itemAt(week);
|
||||
QCOMPARE(weekItem->objectName(), QString::number(week));
|
||||
const QQuickRepeater *weekRepeater = weekItem->findChild<QQuickRepeater *>("week");
|
||||
QVERIFY(weekRepeater);
|
||||
QCOMPARE(weekRepeater->count(), 7);
|
||||
for (int day = 0; day < 7; ++day) {
|
||||
const QVariantMap dayData = weekData[day].toMap();
|
||||
const QQuickItem *dayItem = weekRepeater->itemAt(day);
|
||||
QCOMPARE(dayItem->objectName(), dayData["_day"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tst_qqmllistmodel::nestedLists()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent component(&engine, testFileUrl("nestedLists.qml"));
|
||||
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
||||
QScopedPointer<QObject> o(component.create());
|
||||
QVERIFY(!o.isNull());
|
||||
|
||||
QQuickRepeater *topLevel = o->findChild<QQuickRepeater *>("topLevel");
|
||||
|
||||
const QVariantList list = createVariantModel();
|
||||
QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(list)));
|
||||
verifyLists(list, topLevel);
|
||||
|
||||
const QObjectList objects {
|
||||
new Month(1, o.data()),
|
||||
new Month(2, o.data()),
|
||||
new Month(3, o.data())
|
||||
};
|
||||
|
||||
QMetaObject::invokeMethod(o.data(), "load", Q_ARG(QVariant, QVariant::fromValue(objects)));
|
||||
verifyLists(list, topLevel);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qqmllistmodel)
|
||||
|
||||
#include "tst_qqmllistmodel.moc"
|
||||
|
|
Loading…
Reference in New Issue