aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-01-07 11:01:56 +0100
committerUlf Hermann <ulf.hermann@qt.io>2022-01-28 15:03:00 +0100
commitb0fc028cb5a5dfa9e95640a32e9b38ca6df0734d (patch)
tree7a446acca0f5bcbe4e62a1ac22bdb0185913bc5a
parent8c4c0605b077d63e3d73d34ad6dcc4a2cf607b4c (diff)
QML: Allow named lists of value types
We register QList<T> as sequential container type for any value type T we get. This way we can always find a type to use for list<t> with t being a value type. The metatypes are shuffled around so that we have an easier time associating a type with its list and vice versa. As QQmlPropertyData's isQList flag denotes both QQmlListProperty<T> and QList<T> now, we need to use QMetaType::IsQmlList more often. Conversely, any name given to extra sequential containers registered via QML_SEQUENTIAL_CONTAINER is explicitly ignored now. As you can do list<foo> for any type foo now, there is not much of a point in having further named container registrations for the same type. It would just make things more complicated. Mind that the name had already been ignored before, just not explicitly. [ChangeLog][QtQml] You can now use lists of value types in QML. For example a property of type list<int> will hold a list of integers. Task-number: QTBUG-82443 Change-Id: I7bee61cee3963dae5d231bf59f70b8012984371d Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc26
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp33
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp87
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h4
-rw-r--r--src/qml/qml/qqml.cpp116
-rw-r--r--src/qml/qml/qqml.h60
-rw-r--r--src/qml/qml/qqmlbinding.cpp5
-rw-r--r--src/qml/qml/qqmllist_p.h2
-rw-r--r--src/qml/qml/qqmlmetatype.cpp45
-rw-r--r--src/qml/qml/qqmlmetatype_p.h4
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp8
-rw-r--r--src/qml/qml/qqmlprivate.h20
-rw-r--r--src/qml/qml/qqmlproperty.cpp67
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp20
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h18
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp2
-rw-r--r--src/qml/qml/qqmltype.cpp5
-rw-r--r--src/qml/qml/qqmltype_p.h1
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp363
-rw-r--r--src/qmltyperegistrar/qmltypescreator.cpp6
-rw-r--r--src/qmlworkerscript/qv4serialize.cpp6
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp3
-rw-r--r--tests/auto/qml/qqmllanguage/data/ValueTypeListBase.qml9
-rw-r--r--tests/auto/qml/qqmllanguage/data/valueTypeList.qml22
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp44
26 files changed, 614 insertions, 364 deletions
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
index 804a4c5596..53fe1c2875 100644
--- a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc
@@ -307,7 +307,7 @@ property is only invoked when the property is reassigned to a different object v
\ingroup qmlvaluetypes
\brief a list of QML objects.
- The \c list type refers to a list of QML objects.
+ The \c list type refers to a list of QML objects or values.
A list value can be accessed in a similar way to a JavaScript array:
@@ -320,14 +320,14 @@ property is only invoked when the property is reassigned to a different object v
Values can be dynamically added to the list by using the \c push method,
as if it were a JavaScript Array
- A \c list can only store QML objects, and cannot contain any
- \l {QML Value Types}{value type} values. (To store value types within a
- list, use the \l var type instead.)
+ A \c list can store QML objects or \l{QML Value Types}{value type} values.
When integrating with C++, note that any QQmlListProperty value
\l{qtqml-cppintegration-data.html}{passed into QML from C++} is automatically
converted into a \c list value, and vice-versa.
+ Similarly any \c{QList<T>} of a registered value type \c{T} is automatically
+ converted into a \c list value, and vice-versa.
\section1 Using the list Type
@@ -367,13 +367,17 @@ property is only invoked when the property is reassigned to a different object v
}
\endqml
- Objects in a list can be replaced with the \c{[]} operator, just like
- entries of JavaScript arrays. You can also use \c{push()} to append entries,
- or you can set the \c length property of the list to truncate or extend it.
- You can not automatically extend the list by assigning to an index currently
- out of range, though. Furthermore, if you insert \c null values into the
- list, those are converted to \c nullptr entries in the underlying
- QQmlListProperty.
+ Objects and values in a list can be replaced with the \c{[]} operator, just
+ like entries of JavaScript arrays. You can also use \c{push()} to append
+ entries, or you can set the \c length property of the list to truncate or
+ extend it. You can not automatically extend the list by assigning to an
+ index currently out of range, though. Furthermore, if you insert \c null
+ values into a list of objects, those are converted to \c nullptr entries in
+ the underlying QQmlListProperty.
+
+ A list of value types is different from a JavaScript array in one important
+ aspect: Growing it by setting its length does not produce undefined entries,
+ but rather default-constructed instances of the value type.
This value type is provided by the QML language.
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index a63f1fc7f9..c775d66638 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -131,11 +131,12 @@ static ReturnedValue loadProperty(ExecutionEngine *v4, QObject *object,
Q_ASSERT(!property.isFunction());
Scope scope(v4);
+ const QMetaType propMetaType = property.propType();
if (property.isQObject()) {
QObject *rv = nullptr;
property.readProperty(object, &rv);
ReturnedValue ret = QObjectWrapper::wrap(v4, rv);
- if (property.propType().flags().testFlag(QMetaType::IsConst)) {
+ if (propMetaType.flags().testFlag(QMetaType::IsConst)) {
ScopedValue v(scope, ret);
if (auto obj = v->as<Object>()) {
obj->setInternalClass(obj->internalClass()->cryopreserved());
@@ -145,10 +146,9 @@ static ReturnedValue loadProperty(ExecutionEngine *v4, QObject *object,
return ret;
}
- if (property.isQList())
- return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType());
+ if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList))
+ return QmlListWrapper::create(v4, object, property.coreIndex(), propMetaType);
- const QMetaType propMetaType = property.propType();
switch (property.isEnum() ? QMetaType::Int : propMetaType.id()) {
case QMetaType::Int: {
int v = 0;
@@ -205,23 +205,23 @@ static ReturnedValue loadProperty(ExecutionEngine *v4, QObject *object,
if (QQmlMetaType::isValueType(propMetaType)) {
if (const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(propMetaType))
return QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, propMetaType);
- } else {
- // see if it's a sequence type
- bool succeeded = false;
- ScopedValue retn(scope, SequencePrototype::newSequence(
- v4, propMetaType, object, property.coreIndex(),
- !property.isWritable(), &succeeded));
- if (succeeded)
- return retn->asReturnedValue();
}
+ // see if it's a sequence type
+ bool succeeded = false;
+ QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(
+ v4, propMetaType, object, property.coreIndex(),
+ !property.isWritable(), &succeeded));
+ if (succeeded)
+ return retn->asReturnedValue();
+
if (!propMetaType.isValid()) {
QMetaProperty p = object->metaObject()->property(property.coreIndex());
qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
"'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
return Encode::undefined();
} else {
- QVariant v(property.propType(), (void *)nullptr);
+ QVariant v(propMetaType);
property.readProperty(object, v.data());
return scope.engine->fromVariant(v);
}
@@ -1541,8 +1541,8 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
}
}
- if (auto sequenceMetaType = SequencePrototype::metaTypeForSequence(obj); sequenceMetaType != -1) {
- if (sequenceMetaType == conversionType)
+ if (auto sequenceMetaType = SequencePrototype::metaTypeForSequence(obj); sequenceMetaType.isValid()) {
+ if (sequenceMetaType == conversionMetaType)
return 1;
else
return 10;
@@ -1888,7 +1888,8 @@ bool CallArgument::fromContainerValue(const Value &value, M CallArgument::*membe
{
const Object *object = value.as<Object>();
if (object && object->isListType()) {
- if (T* ptr = static_cast<T *>(SequencePrototype::getRawContainerPtr(object, type))) {
+ if (T* ptr = static_cast<T *>(SequencePrototype::getRawContainerPtr(
+ object, QMetaType(type)))) {
(this->*member) = ptr;
return true;
}
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 7d4c91bb50..42ad6b8716 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -81,7 +81,7 @@ struct QV4Sequence : Object {
void init(const QQmlType &qmlType, const void *container);
void init(QObject *object, int propertyIndex, const QQmlType &qmlType, bool readOnly);
void destroy() {
- typePrivate->typeId.destroy(container);
+ typePrivate->listId.destroy(container);
QQmlType::derefHandle(typePrivate);
object.destroy();
Object::destroy();
@@ -110,31 +110,41 @@ struct QV4Sequence : public QV4::Object
V4_NEEDS_DESTROY
public:
+ static const QMetaSequence *metaSequence(const Heap::QV4Sequence *p)
+ {
+ return p->typePrivate->extraData.ld;
+ }
+
+ static const QMetaType valueMetaType(const Heap::QV4Sequence *p)
+ {
+ return p->typePrivate->typeId;
+ }
+
qsizetype size() const
{
const auto *p = d();
- return meta(p)->size(p->container);
+ return metaSequence(p)->size(p->container);
}
QVariant at(int index) const
{
const auto *p = d();
- const auto *m = meta(p);
- QVariant result(m->valueMetaType());
- m->valueAtIndex(p->container, index, result.data());
+ QVariant result(valueMetaType(p));
+ metaSequence(p)->valueAtIndex(p->container, index, result.data());
return result;
}
void append(const QVariant &item)
{
const auto *p = d();
- const auto *m = meta(p);
- if (item.metaType() == m->valueMetaType()) {
+ const auto *m = metaSequence(p);
+ const QMetaType v = valueMetaType(p);
+ if (item.metaType() == v) {
m->addValueAtEnd(p->container, item.constData());
} else {
QVariant converted = item;
- if (!converted.convert(m->valueMetaType()))
- converted = QVariant(m->valueMetaType());
+ if (!converted.convert(v))
+ converted = QVariant(v);
m->addValueAtEnd(p->container, converted.constData());
}
}
@@ -142,13 +152,14 @@ public:
void replace(int index, const QVariant &item)
{
const auto *p = d();
- const auto *m = meta(p);
- if (item.metaType() == m->valueMetaType()) {
+ const auto *m = metaSequence(p);
+ const QMetaType v = valueMetaType(p);
+ if (item.metaType() == v) {
m->setValueAtIndex(p->container, index, item.constData());
} else {
QVariant converted = item;
- if (!converted.convert(m->valueMetaType()))
- converted = QVariant(m->valueMetaType());
+ if (!converted.convert(v))
+ converted = QVariant(v);
m->setValueAtIndex(p->container, index, converted.constData());
}
}
@@ -157,9 +168,9 @@ public:
void sort(const Compare &compare)
{
const auto *p = d();
- const auto *m = meta(p);
+ const auto *m = metaSequence(p);
- QSequentialIterable iterable(*m, p->typePrivate->typeId, p->container);
+ QSequentialIterable iterable(*m, p->typePrivate->listId, p->container);
if (iterable.canRandomAccessIterate()) {
std::sort(QSequentialIterable::RandomAccessIterator(iterable.mutableBegin()),
QSequentialIterable::RandomAccessIterator(iterable.mutableEnd()),
@@ -176,7 +187,7 @@ public:
void removeLast(int num)
{
const auto *p = d();
- const auto *m = meta(p);
+ const auto *m = metaSequence(p);
if (m->canEraseRangeAtIterator() && m->hasRandomAccessIterator() && num > 1) {
void *i = m->end(p->container);
@@ -194,7 +205,7 @@ public:
QVariant toVariant()
{
const auto *p = d();
- return QVariant(p->typePrivate->typeId, p->container);
+ return QVariant(p->typePrivate->listId, p->container);
}
// ### Qt 7 use qsizetype instead.
@@ -249,8 +260,8 @@ public:
}
quint32 count = quint32(size());
- const QMetaType valueMetaType = meta(d())->valueMetaType();
- const QVariant element = engine()->toVariant(value, valueMetaType, false);
+ const QMetaType valueType = valueMetaType(d());
+ const QVariant element = engine()->toVariant(value, valueType, false);
if (index == count) {
append(element);
@@ -260,7 +271,7 @@ public:
/* according to ECMA262r3 we need to insert */
/* the value at the given index, increasing length to index+1. */
while (index > count++)
- append(QVariant(valueMetaType));
+ append(QVariant(valueType));
append(element);
}
@@ -471,7 +482,7 @@ void Heap::QV4Sequence::init(const QQmlType &qmlType, const void *container)
typePrivate = qmlType.priv();
QQmlType::refHandle(typePrivate);
- this->container = QMetaType(typePrivate->typeId).create(container);
+ this->container = typePrivate->listId.create(container);
propertyIndex = -1;
isReference = false;
isReadOnly = false;
@@ -490,7 +501,7 @@ void Heap::QV4Sequence::init(QObject *object, int propertyIndex, const QQmlType
Q_ASSERT(qmlType.isSequentialContainer());
typePrivate = qmlType.priv();
QQmlType::refHandle(typePrivate);
- container = QMetaType(typePrivate->typeId).create();
+ container = QMetaType(typePrivate->listId).create();
this->propertyIndex = propertyIndex;
isReference = true;
this->isReadOnly = readOnly;
@@ -571,7 +582,6 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value
RETURN_UNDEFINED();
}
-
void SequencePrototype::init()
{
defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
@@ -612,7 +622,7 @@ ReturnedValue SequencePrototype::newSequence(
// (as well as object ptr + property index for updated-read and write-back)
// and so access/mutate avoids variant conversion.
- const QQmlType qmlType = QQmlMetaType::qmlType(sequenceType);
+ const QQmlType qmlType = QQmlMetaType::qmlListType(sequenceType);
if (qmlType.isSequentialContainer()) {
*succeeded = true;
QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QV4Sequence>(
@@ -638,7 +648,7 @@ ReturnedValue SequencePrototype::fromData(ExecutionEngine *engine, QMetaType typ
// Access and mutation is extremely fast since it will not need to modify any
// QObject property.
- const QQmlType qmlType = QQmlMetaType::qmlType(type);
+ const QQmlType qmlType = QQmlMetaType::qmlListType(type);
if (qmlType.isSequentialContainer()) {
*succeeded = true;
QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QV4Sequence>(qmlType, data));
@@ -666,18 +676,25 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
QV4::Scope scope(array.as<Object>()->engine());
QV4::ScopedArrayObject a(scope, array);
- const QQmlType type = QQmlMetaType::qmlType(typeHint);
+ const QQmlType type = QQmlMetaType::qmlListType(typeHint);
if (type.isSequentialContainer()) {
- const QMetaSequence *meta = type.priv()->extraData.ld;
- const QMetaType containerMetaType(type.priv()->typeId);
+ const QQmlTypePrivate *priv = type.priv();
+ const QMetaSequence *meta = priv->extraData.ld;
+ const QMetaType containerMetaType(priv->listId);
QVariant result(containerMetaType);
quint32 length = a->getLength();
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < length; ++i) {
- const QMetaType valueMetaType = meta->valueMetaType();
+ const QMetaType valueMetaType = priv->typeId;
QVariant variant = scope.engine->toVariant(a->get(i), valueMetaType, false);
- if (variant.metaType() != valueMetaType && !variant.convert(valueMetaType))
+ const QMetaType originalType = variant.metaType();
+ if (originalType != valueMetaType && !variant.convert(valueMetaType)) {
+ qWarning() << QLatin1String(
+ "Could not convert array value at position %1 from %2 to %3")
+ .arg(QString::number(i), QString::fromUtf8(originalType.name()),
+ QString::fromUtf8(valueMetaType.name()));
variant = QVariant(valueMetaType);
+ }
meta->addValueAtEnd(result.data(), variant.constData());
}
return result;
@@ -687,20 +704,20 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
return QVariant();
}
-void* SequencePrototype::getRawContainerPtr(const Object *object, int typeHint)
+void *SequencePrototype::getRawContainerPtr(const Object *object, QMetaType typeHint)
{
if (auto *s = object->as<QV4Sequence>()) {
- if (s->d()->typePrivate->typeId.id() == typeHint)
+ if (s->d()->typePrivate->listId == typeHint)
return s->getRawContainerPtr();
}
return nullptr;
}
-int SequencePrototype::metaTypeForSequence(const QV4::Object *object)
+QMetaType SequencePrototype::metaTypeForSequence(const QV4::Object *object)
{
if (auto *s = object->as<QV4Sequence>())
- return s->d()->typePrivate->typeId.id();
- return -1;
+ return s->d()->typePrivate->listId;
+ return QMetaType();
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index db489d039d..593a6696fb 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -81,10 +81,10 @@ struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object
static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant &v, bool *succeeded);
static ReturnedValue fromData(QV4::ExecutionEngine *engine, QMetaType type, const void *data, bool *succeeded);
- static int metaTypeForSequence(const Object *object);
+ static QMetaType metaTypeForSequence(const Object *object);
static QVariant toVariant(Object *object);
static QVariant toVariant(const Value &array, QMetaType typeHint, bool *succeeded);
- static void* getRawContainerPtr(const Object *object, int typeHint);
+ static void *getRawContainerPtr(const Object *object, QMetaType typeHint);
};
}
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 98eb7e40c3..cfcc8d5a2b 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -479,7 +479,7 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
noCreateReason = QLatin1String("Type cannot be created in QML.");
}
- RegisterType revisionRegistration = {
+ RegisterType typeRevision = {
1,
type.typeId,
type.listId,
@@ -504,6 +504,16 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
type.structVersion > 0 ? type.finalizerCast : -1
};
+ QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
+ 0,
+ type.uri,
+ type.version,
+ nullptr,
+ type.listId,
+ type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
+ QTypeRevision(),
+ };
+
const QTypeRevision added = revisionClassInfo(
type.classInfoMetaObject, "QML.AddedInVersion",
QTypeRevision::fromMinorVersion(0));
@@ -524,19 +534,28 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
break;
// When removed, we still add revisions, but anonymous ones
if (removed.isValid() && !(revision < removed)) {
- revisionRegistration.elementName = nullptr;
- revisionRegistration.create = nullptr;
+ typeRevision.elementName = nullptr;
+ typeRevision.create = nullptr;
} else {
- revisionRegistration.elementName = elementName;
- revisionRegistration.create = creatable ? type.create : nullptr;
- revisionRegistration.userdata = type.userdata;
+ typeRevision.elementName = elementName;
+ typeRevision.create = creatable ? type.create : nullptr;
+ typeRevision.userdata = type.userdata;
}
- assignVersions(&revisionRegistration, revision, type.version);
- revisionRegistration.customParser = type.customParserFactory();
- const int id = qmlregister(TypeRegistration, &revisionRegistration);
+ assignVersions(&typeRevision, revision, type.version);
+ typeRevision.customParser = type.customParserFactory();
+ const int id = qmlregister(TypeRegistration, &typeRevision);
if (type.qmlTypeIds)
type.qmlTypeIds->append(id);
+
+ if (sequenceRevision.metaSequence != QMetaSequence()) {
+ sequenceRevision.version = typeRevision.version;
+ sequenceRevision.revision = typeRevision.revision;
+ const int id = QQmlPrivate::qmlregister(
+ QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+ }
}
break;
}
@@ -594,12 +613,11 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
case SequentialContainerAndRevisionsRegistration: {
const RegisterSequentialContainerAndRevisions &type
= *reinterpret_cast<RegisterSequentialContainerAndRevisions *>(data);
- const char *elementName = classElementName(type.classInfoMetaObject);
RegisterSequentialContainer revisionRegistration = {
0,
type.uri,
type.version,
- elementName,
+ nullptr,
type.typeId,
type.metaSequence,
QTypeRevision()
@@ -608,10 +626,8 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
const QTypeRevision added = revisionClassInfo(
type.classInfoMetaObject, "QML.AddedInVersion",
QTypeRevision::fromMinorVersion(0));
- const QTypeRevision removed = revisionClassInfo(
- type.classInfoMetaObject, "QML.RemovedInVersion");
- QList<QTypeRevision> revisions = revisionClassInfos(type.classInfoMetaObject,
- "QML.ExtraVersion");
+ QList<QTypeRevision> revisions = revisionClassInfos(
+ type.classInfoMetaObject, "QML.ExtraVersion");
revisions.append(added);
uniqueRevisions(&revisions, type.version, added);
@@ -621,12 +637,6 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
break;
- // When removed, we still add revisions, but anonymous ones
- if (removed.isValid() && !(revision < removed))
- revisionRegistration.typeName = nullptr;
- else
- revisionRegistration.typeName = elementName;
-
assignVersions(&revisionRegistration, revision, type.version);
const int id = qmlregister(SequentialContainerRegistration, &revisionRegistration);
if (type.qmlTypeIds)
@@ -697,40 +707,42 @@ void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
namespace QQmlPrivate {
template<>
-void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(const char *uri, int versionMajor,
- const QMetaObject *classInfoMetaObject,
- QVector<int> *qmlTypeIds,
- const QMetaObject *extension, bool)
+void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
+ const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
+ QVector<int> *qmlTypeIds, const QMetaObject *extension, bool)
{
using T = QQmlTypeNotAvailable;
- RegisterTypeAndRevisions type = { 1,
- QMetaType::fromType<T *>(),
- QMetaType::fromType<QQmlListProperty<T>>(),
- 0,
- nullptr,
- nullptr,
- nullptr,
-
- uri,
- QTypeRevision::fromMajorVersion(versionMajor),
-
- &QQmlTypeNotAvailable::staticMetaObject,
- classInfoMetaObject,
-
- attachedPropertiesFunc<T>(),
- attachedPropertiesMetaObject<T>(),
-
- StaticCastSelector<T, QQmlParserStatus>::cast(),
- StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
- StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
-
- nullptr,
- extension,
- qmlCreateCustomParser<T>,
- qmlTypeIds,
- QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
- false };
+ RegisterTypeAndRevisions type = {
+ 3,
+ QmlMetaType<T>::self(),
+ QmlMetaType<T>::list(),
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+
+ uri,
+ QTypeRevision::fromMajorVersion(versionMajor),
+
+ &QQmlTypeNotAvailable::staticMetaObject,
+ classInfoMetaObject,
+
+ attachedPropertiesFunc<T>(),
+ attachedPropertiesMetaObject<T>(),
+
+ StaticCastSelector<T, QQmlParserStatus>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
+
+ nullptr,
+ extension,
+ qmlCreateCustomParser<T>,
+ qmlTypeIds,
+ QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
+ false,
+ QmlMetaType<T>::sequence(),
+ };
qmlregister(TypeAndRevisionsRegistration, &type);
}
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index e205821bbf..b20b8dc5d3 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -843,35 +843,37 @@ inline void qmlRegisterNamespaceAndRevisions(const QMetaObject *metaObject,
const QMetaObject *classInfoMetaObject,
const QMetaObject *extensionMetaObject)
{
- QQmlPrivate::RegisterTypeAndRevisions type = { 1,
- QMetaType(),
- QMetaType(),
- 0,
- nullptr,
- nullptr,
- nullptr,
-
- uri,
- QTypeRevision::fromMajorVersion(versionMajor),
-
- metaObject,
- (classInfoMetaObject ? classInfoMetaObject
- : metaObject),
-
- nullptr,
- nullptr,
-
- -1,
- -1,
- -1,
-
- nullptr,
- extensionMetaObject,
-
- &qmlCreateCustomParser<void>,
- qmlTypeIds,
- -1,
- false };
+ QQmlPrivate::RegisterTypeAndRevisions type = {
+ 3,
+ QMetaType(),
+ QMetaType(),
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+
+ uri,
+ QTypeRevision::fromMajorVersion(versionMajor),
+
+ metaObject,
+ (classInfoMetaObject ? classInfoMetaObject : metaObject),
+
+ nullptr,
+ nullptr,
+
+ -1,
+ -1,
+ -1,
+
+ nullptr,
+ extensionMetaObject,
+
+ &qmlCreateCustomParser<void>,
+ qmlTypeIds,
+ -1,
+ false,
+ QMetaSequence()
+ };
qmlregister(QQmlPrivate::TypeAndRevisionsRegistration, &type);
}
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 54a188dbb0..6bb370eab7 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -530,7 +530,10 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
if (isUndefined) {
} else if (core.isQList()) {
- value = v4engine->toVariant(result, QMetaType::fromType<QList<QObject *> >());
+ if (core.propType().flags() & QMetaType::IsQmlList)
+ value = v4engine->toVariant(result, QMetaType::fromType<QList<QObject *> >());
+ else
+ value = v4engine->toVariant(result, core.propType());
} else if (result.isNull() && core.isQObject()) {
value = QVariant::fromValue((QObject *)nullptr);
} else if (core.propType() == QMetaType::fromType<QList<QUrl>>()) {
diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h
index b0ae94655d..30c8e8e090 100644
--- a/src/qml/qml/qqmllist_p.h
+++ b/src/qml/qml/qqmllist_p.h
@@ -82,7 +82,7 @@ public:
{
if (!m_elementType) {
m_elementType = QQmlMetaType::rawMetaObjectForType(
- QQmlMetaType::listType(propertyType)).metaObject();
+ QQmlMetaType::listValueType(propertyType)).metaObject();
}
return m_elementType;
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index f8cf142f5e..fb0e2af578 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -475,12 +475,15 @@ static void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data)
if (type->baseMetaObject)
data->metaObjectToType.insert(type->baseMetaObject, type);
- if (type->typeId.isValid()) {
- data->idToType.insert(type->typeId.id(), type);
- }
+ if (type->regType == QQmlType::SequentialContainerType) {
+ if (type->listId.isValid())
+ data->idToType.insert(type->listId.id(), type);
+ } else {
+ if (type->typeId.isValid())
+ data->idToType.insert(type->typeId.id(), type);
- if (type->listId.isValid()) {
- data->idToType.insert(type->listId.id(), type);
+ if (type->listId.flags().testFlag(QMetaType::IsQmlList))
+ data->idToType.insert(type->listId.id(), type);
}
if (!type->module.isEmpty()) {
@@ -637,8 +640,7 @@ QQmlType QQmlMetaType::registerSequentialContainer(
QQmlMetaTypeDataPtr data;
- const QString typeName = QString::fromUtf8(container.typeName);
- if (!checkRegistration(QQmlType::SequentialContainerType, data, container.uri, typeName,
+ if (!checkRegistration(QQmlType::SequentialContainerType, data, container.uri, QString(),
container.version, {})) {
return QQmlType();
}
@@ -646,10 +648,11 @@ QQmlType QQmlMetaType::registerSequentialContainer(
QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::SequentialContainerType);
data->registerType(priv);
- priv->setName(QString::fromUtf8(container.uri), typeName);
+ priv->setName(QString::fromUtf8(container.uri), QString());
priv->version = container.version;
priv->revision = container.revision;
- priv->typeId = container.typeId;
+ priv->typeId = container.metaSequence.valueMetaType();
+ priv->listId = container.typeId;
*priv->extraData.ld = container.metaSequence;
addTypeToData(priv, data);
@@ -1067,13 +1070,16 @@ QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok)
/*
Returns the item type for a list of type \a id.
*/
-QMetaType QQmlMetaType::listType(QMetaType metaType)
-{
- if (!isList(metaType))
- return QMetaType {};
- const auto iface = metaType.iface();
- if (iface->metaObjectFn == &dynamicQmlListMarker)
- return QMetaType(static_cast<const QQmlListMetaTypeInterface *>(iface)->valueType);
+QMetaType QQmlMetaType::listValueType(QMetaType metaType)
+{
+ if (isList(metaType)) {
+ const auto iface = metaType.iface();
+ if (iface->metaObjectFn == &dynamicQmlListMarker)
+ return QMetaType(static_cast<const QQmlListMetaTypeInterface *>(iface)->valueType);
+ } else if (metaType.flags() & QMetaType::PointerToQObject) {
+ return QMetaType();
+ }
+
QQmlMetaTypeDataPtr data;
QQmlTypePrivate *type = data->idToType.value(metaType.id());
if (type && type->listId == metaType)
@@ -1258,6 +1264,13 @@ QQmlType QQmlMetaType::qmlType(QMetaType metaType)
return (type && type->typeId == metaType) ? QQmlType(type) : QQmlType();
}
+QQmlType QQmlMetaType::qmlListType(QMetaType metaType)
+{
+ const QQmlMetaTypeDataPtr data;
+ QQmlTypePrivate *type = data->idToType.value(metaType.id());
+ return (type && type->listId == metaType) ? QQmlType(type) : QQmlType();
+}
+
/*!
Returns the type (if any) that corresponds to the given \a url in the set of
composite types added through file imports.
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index aaa8bafada..1fb4d915da 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -172,6 +172,8 @@ public:
static QQmlType qmlTypeById(int qmlTypeId);
static QQmlType qmlType(QMetaType metaType);
+ static QQmlType qmlListType(QMetaType metaType);
+
static QQmlType qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports = false);
static QQmlRefPointer<QQmlPropertyCache> propertyCache(
@@ -198,7 +200,7 @@ public:
static QObject *toQObject(const QVariant &, bool *ok = nullptr);
- static QMetaType listType(QMetaType type);
+ static QMetaType listValueType(QMetaType type);
static QQmlAttachedPropertiesFunc attachedPropertiesFunc(QQmlEnginePrivate *,
const QMetaObject *);
static bool isInterface(QMetaType type);
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index ce14f87ec5..b04de3a208 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -259,7 +259,7 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
const QQmlPropertyData &property = qmlProperty->core;
- if (property.isQList()) {
+ if (property.propType().flags().testFlag(QMetaType::IsQmlList)) {
void *argv[1] = { (void*)&_currentList };
QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property.coreIndex(), argv);
} else if (_currentList.object) {
@@ -693,7 +693,7 @@ void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
continue;
}
- if (property && property->isQList()) {
+ if (property && property->propType().flags().testFlag(QMetaType::IsQmlList)) {
if (property->coreIndex() != currentListPropertyIndex) {
void *argv[1] = { (void*)&_currentList };
QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv);
@@ -1081,12 +1081,12 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
argv[0] = &value;
QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv);
}
- } else if (bindingProperty->isQList()) {
+ } else if (bindingProperty->propType().flags().testFlag(QMetaType::IsQmlList)) {
Q_ASSERT(_currentList.object);
void *itemToAdd = createdSubObject;
- QMetaType listItemType = QQmlMetaType::listType(bindingProperty->propType());
+ QMetaType listItemType = QQmlMetaType::listValueType(bindingProperty->propType());
if (listItemType.isValid()) {
const char *iid = QQmlMetaType::interfaceIId(listItemType);
if (iid)
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 9539730d53..108b23b827 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -513,6 +513,7 @@ namespace QQmlPrivate
int finalizerCast;
bool forceAnonymous;
+ QMetaSequence listMetaSequence;
};
struct RegisterInterface {
@@ -590,7 +591,11 @@ namespace QQmlPrivate
int structVersion;
const char *uri;
QTypeRevision version;
+
+ // ### Qt7: Remove typeName. It's ignored because the only valid name is "list",
+ // and that's automatic.
const char *typeName;
+
QMetaType typeId;
QMetaSequence metaSequence;
QTypeRevision revision;
@@ -931,7 +936,15 @@ namespace QQmlPrivate
if constexpr (std::is_base_of_v<QObject, T>)
return QMetaType::fromType<QQmlListProperty<T>>();
else
- return QMetaType();
+ return QMetaType::fromType<QList<T>>();
+ }
+
+ static QMetaSequence sequence()
+ {
+ if constexpr (std::is_base_of_v<QObject, T>)
+ return QMetaSequence();
+ else
+ return QMetaSequence::fromContainer<QList<T>>();
}
};
@@ -973,7 +986,7 @@ namespace QQmlPrivate
static_assert(std::is_base_of_v<QObject, T> || !QQmlTypeInfo<T>::hasAttachedProperties);
RegisterTypeAndRevisions type = {
- 2,
+ 3,
QmlMetaType<T>::self(),
QmlMetaType<T>::list(),
int(sizeof(T)),
@@ -1001,7 +1014,8 @@ namespace QQmlPrivate
qmlTypeIds,
StaticCastSelector<T, QQmlFinalizerHook>::cast(),
- forceAnonymous
+ forceAnonymous,
+ QmlMetaType<T>::sequence(),
};
// Initialize the extension so that we can find it by name or ID.
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index 31cc9374b4..7a49f53e9d 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -1462,43 +1462,54 @@ bool QQmlPropertyPrivate::write(
: urlSequence(value);
return property.writeProperty(object, &urlSeq, flags);
} else if (property.isQList()) {
- QQmlMetaObject listType;
-
- listType = QQmlMetaType::rawMetaObjectForType(QQmlMetaType::listType(propertyMetaType));
- if (listType.isNull())
- return false;
+ if (propertyMetaType.flags() & QMetaType::IsQmlList) {
+ QMetaType listValueType = QQmlMetaType::listValueType(propertyMetaType);
+ QQmlMetaObject valueMetaObject = QQmlMetaType::rawMetaObjectForType(listValueType);
+ if (valueMetaObject.isNull())
+ return false;
- QQmlListProperty<void> prop;
- property.readProperty(object, &prop);
+ QQmlListProperty<void> prop;
+ property.readProperty(object, &prop);
- if (!prop.clear)
- return false;
+ if (!prop.clear)
+ return false;
- prop.clear(&prop);
+ prop.clear(&prop);
- if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
- QQmlListReference qdlr = value.value<QQmlListReference>();
+ if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
+ QQmlListReference qdlr = value.value<QQmlListReference>();
- for (qsizetype ii = 0; ii < qdlr.count(); ++ii) {
- QObject *o = qdlr.at(ii);
- if (o && !QQmlMetaObject::canConvert(o, listType))
- o = nullptr;
- prop.append(&prop, o);
- }
- } else if (variantMetaType == QMetaType::fromType<QList<QObject *>>()) {
- const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
-
- for (qsizetype ii = 0; ii < list.count(); ++ii) {
- QObject *o = list.at(ii);
- if (o && !QQmlMetaObject::canConvert(o, listType))
+ for (qsizetype ii = 0; ii < qdlr.count(); ++ii) {
+ QObject *o = qdlr.at(ii);
+ if (o && !QQmlMetaObject::canConvert(o, valueMetaObject))
+ o = nullptr;
+ prop.append(&prop, o);
+ }
+ } else if (variantMetaType == QMetaType::fromType<QList<QObject *>>()) {
+ const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
+
+ for (qsizetype ii = 0; ii < list.count(); ++ii) {
+ QObject *o = list.at(ii);
+ if (o && !QQmlMetaObject::canConvert(o, valueMetaObject))
+ o = nullptr;
+ prop.append(&prop, o);
+ }
+ } else {
+ QObject *o = QQmlMetaType::toQObject(value);
+ if (o && !QQmlMetaObject::canConvert(o, valueMetaObject))
o = nullptr;
prop.append(&prop, o);
}
+ } else if (variantMetaType == propertyMetaType) {
+ QVariant v = value;
+ property.writeProperty(object, v.data(), flags);
} else {
- QObject *o = QQmlMetaType::toQObject(value);
- if (o && !QQmlMetaObject::canConvert(o, listType))
- o = nullptr;
- prop.append(&prop, o);
+ QVariant list(propertyMetaType);
+ const QQmlType type = QQmlMetaType::qmlType(propertyMetaType);
+ const QMetaSequence sequence = type.listMetaSequence();
+ if (sequence.canAddValue())
+ sequence.addValue(list.data(), value.data());
+ property.writeProperty(object, list.data(), flags);
}
} else if (variantMetaType == QMetaType::fromType<QJSValue>()) {
QJSValue jsValue = qvariant_cast<QJSValue>(value);
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index 60739a8983..de2b7eefe7 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -66,6 +66,26 @@ QMetaType QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledDat
return QMetaType {};
}
+QMetaType QQmlPropertyCacheCreatorBase::listTypeForPropertyType(QV4::CompiledData::BuiltinType type)
+{
+ switch (type) {
+ case QV4::CompiledData::BuiltinType::Var: return QMetaType::fromType<QList<QVariant>>();
+ case QV4::CompiledData::BuiltinType::Int: return QMetaType::fromType<QList<int>>();
+ case QV4::CompiledData::BuiltinType::Bool: return QMetaType::fromType<QList<bool>>();
+ case QV4::CompiledData::BuiltinType::Real: return QMetaType::fromType<QList<qreal>>();
+ case QV4::CompiledData::BuiltinType::String: return QMetaType::fromType<QList<QString>>();
+ case QV4::CompiledData::BuiltinType::Url: return QMetaType::fromType<QList<QUrl>>();
+ case QV4::CompiledData::BuiltinType::Time: return QMetaType::fromType<QList<QTime>>();
+ case QV4::CompiledData::BuiltinType::Date: return QMetaType::fromType<QList<QDate>>();
+ case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
+ case QV4::CompiledData::BuiltinType::Rect: return QMetaType::fromType<QList<QRectF>>();
+ case QV4::CompiledData::BuiltinType::Point: return QMetaType::fromType<QList<QPointF>>();
+ case QV4::CompiledData::BuiltinType::Size: return QMetaType::fromType<QList<QSizeF>>();
+ case QV4::CompiledData::BuiltinType::InvalidBuiltin: break;
+ };
+ return QMetaType {};
+}
+
QByteArray QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(const QUrl &url)
{
const QString path = url.path();
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index e74e91c638..dc1a55c005 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -102,6 +102,7 @@ public:
static QAtomicInt classIndexCounter;
static QMetaType metaTypeForPropertyType(QV4::CompiledData::BuiltinType type);
+ static QMetaType listTypeForPropertyType(QV4::CompiledData::BuiltinType type);
static QByteArray createClassNameTypeByUrl(const QUrl &url);
@@ -650,11 +651,15 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
const QV4::CompiledData::BuiltinType type = p->builtinType();
- if (type == QV4::CompiledData::BuiltinType::Var)
+ if (p->isList)
+ propertyFlags.type = QQmlPropertyData::Flags::QListType;
+ else if (type == QV4::CompiledData::BuiltinType::Var)
propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) {
- propertyType = metaTypeForPropertyType(type);
+ propertyType = p->isList
+ ? listTypeForPropertyType(type)
+ : metaTypeForPropertyType(type);
} else {
Q_ASSERT(!p->isBuiltinType);
@@ -698,12 +703,11 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
propertyType = typeIds.id;
}
} else {
- if (p->isList) {
+ if (p->isList)
propertyType = qmltype.qListTypeId();
- } else {
+ else
propertyType = qmltype.typeId();
- propertyTypeVersion = qmltype.version();
- }
+ propertyTypeVersion = qmltype.version();
}
if (p->isList)
@@ -714,7 +718,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int
propertyFlags.type = QQmlPropertyData::Flags::ValueType;
}
- if (!p->isReadOnly && !p->isList)
+ if (!p->isReadOnly && !propertyType.flags().testFlag(QMetaType::IsQmlList))
propertyFlags.setIsWritable(true);
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index 936f35190c..558a7e87a1 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -745,7 +745,7 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *propert
// We can convert everything to QVariant :)
return noError;
} else if (property->isQList()) {
- const QMetaType listType = QQmlMetaType::listType(property->propType());
+ const QMetaType listType = QQmlMetaType::listValueType(property->propType());
if (!QQmlMetaType::isInterface(listType)) {
QQmlRefPointer<QQmlPropertyCache> source = propertyCaches.at(binding->value.objectIndex);
if (!canCoerce(listType, source)) {
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index f827a24660..aeea0e45ce 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -642,6 +642,11 @@ QMetaType QQmlType::qListTypeId() const
return d ? d->listId : QMetaType{};
}
+QMetaSequence QQmlType::listMetaSequence() const
+{
+ return isSequentialContainer() ? *d->extraData.ld : QMetaSequence();
+}
+
const QMetaObject *QQmlType::metaObject() const
{
if (!d)
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index a7e60743b7..56693efb33 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -132,6 +132,7 @@ public:
QMetaType typeId() const;
QMetaType qListTypeId() const;
+ QMetaSequence listMetaSequence() const;
const QMetaObject *metaObject() const;
const QMetaObject *baseMetaObject() const;
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index a4ad9d2327..a5c55958d4 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -492,7 +492,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
const QString propertyName = stringAt(binding->propertyNameIndex);
bool notInRevision = false;
QQmlPropertyData *pd = resolver.property(propertyName, &notInRevision);
- if (!pd)
+ if (!pd || pd->isQList())
continue;
if (!pd->isEnum() && pd->propType().id() != QMetaType::Int)
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 70c6b2e45e..93abe3c83a 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -58,6 +58,7 @@
#include <private/qv4scopedvalue_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4sequenceobject_p.h>
#include <private/qqmlpropertycachecreator_p.h>
#include <private/qqmlpropertycachemethodarguments_p.h>
@@ -713,54 +714,16 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
: QQmlEnginePrivate::get(ctxt->engine());
if (c == QMetaObject::ReadProperty) {
- switch (t) {
- case QV4::CompiledData::BuiltinType::Int:
- *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
- break;
- case QV4::CompiledData::BuiltinType::Bool:
- *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
- break;
- case QV4::CompiledData::BuiltinType::Real:
- *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
- break;
- case QV4::CompiledData::BuiltinType::String:
- *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
- break;
- case QV4::CompiledData::BuiltinType::Url:
- *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
- break;
- case QV4::CompiledData::BuiltinType::Date:
- *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
- break;
- case QV4::CompiledData::BuiltinType::DateTime:
- *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
- break;
- case QV4::CompiledData::BuiltinType::Rect:
- *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
- break;
- case QV4::CompiledData::BuiltinType::Size:
- *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
- break;
- case QV4::CompiledData::BuiltinType::Point:
- *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
- break;
- case QV4::CompiledData::BuiltinType::Time:
- *reinterpret_cast<QTime *>(a[0]) = readPropertyAsTime(id);
- break;
- case QV4::CompiledData::BuiltinType::Var:
- if (ep) {
- *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
- } else {
- // if the context was disposed, we just return an invalid variant from read.
- *reinterpret_cast<QVariant *>(a[0]) = QVariant();
- }
- break;
- case QV4::CompiledData::BuiltinType::InvalidBuiltin:
- if (property.isList) {
+ if (property.isList) {
+ // _id because this is an absolute property ID.
+ const QQmlPropertyData *propertyData = cache->property(_id);
+ const QMetaType propType = propertyData->propType();
+
+ if (propType.flags().testFlag(QMetaType::IsQmlList)) {
// when reading from the list, we need to find the correct MetaObject,
- // namely this. However, obejct->metaObject might point to any MetaObject
- // down the inheritance hierarchy, so we need to store how far we have
- // to go down
+ // namely this. However, obejct->metaObject might point to any
+ // MetaObject down the inheritance hierarchy, so we need to store how
+ // far we have to go down
// To do this, we encode the hierarchy depth together with the id of the
// property in a single quintptr, with the first half storing the depth
// and the second half storing the property id
@@ -771,16 +734,18 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
mo = mo->parentVMEMetaObject();
++inheritanceDepth;
}
- constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
- if (Q_UNLIKELY(inheritanceDepth >= (quintptr(1) << quintptr(usableBits / 2u) ) )) {
- qmlWarning(object) << "Too many objects in inheritance hierarchy for list property";
+ constexpr quintptr idBits = sizeof(quintptr) * CHAR_BIT / 2u;
+ if (Q_UNLIKELY(inheritanceDepth >= (quintptr(1) << idBits))) {
+ qmlWarning(object) << "Too many objects in inheritance hierarchy "
+ "for list property";
return -1;
}
- if (Q_UNLIKELY(quintptr(id) >= (quintptr(1) << quintptr(usableBits / 2) ) )) {
- qmlWarning(object) << "Too many properties in object for list property";
+ if (Q_UNLIKELY(quintptr(id) >= (quintptr(1) << idBits))) {
+ qmlWarning(object) << "Too many properties in object "
+ "for list property";
return -1;
}
- quintptr encodedIndex = (inheritanceDepth << (usableBits/2)) + id;
+ quintptr encodedIndex = (inheritanceDepth << idBits) + id;
readPropertyAsList(id); // Initializes if necessary
*static_cast<QQmlListProperty<QObject> *>(a[0])
@@ -789,123 +754,215 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
list_append, list_count, list_at,
list_clear, list_replace, list_removeLast);
} else if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
+ // Value type list
QV4::Scope scope(engine);
- QV4::ScopedValue sv(scope, *(md->data() + id));
-
- // _id because this is an absolute property ID.
- const QQmlPropertyData *propertyData = cache->property(_id);
-
- if (propertyData->isQObject()) {
- if (const auto *wrap = sv->as<QV4::QObjectWrapper>())
- *reinterpret_cast<QObject **>(a[0]) = wrap->object();
- else
- *reinterpret_cast<QObject **>(a[0]) = nullptr;
+ QV4::ScopedObject sequence(scope, *(md->data() + id));
+ const void *data = sequence
+ ? QV4::SequencePrototype::getRawContainerPtr(sequence, propType)
+ : nullptr;
+ propType.destruct(a[0]);
+ propType.construct(a[0], data);
+ } else {
+ qmlWarning(object) << "Cannot find member data";
+ }
+ } else {
+ switch (t) {
+ case QV4::CompiledData::BuiltinType::Int:
+ *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Bool:
+ *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Real:
+ *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
+ break;
+ case QV4::CompiledData::BuiltinType::String:
+ *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Url:
+ *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Date:
+ *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
+ break;
+ case QV4::CompiledData::BuiltinType::DateTime:
+ *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Rect:
+ *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Size:
+ *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Point:
+ *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Time:
+ *reinterpret_cast<QTime *>(a[0]) = readPropertyAsTime(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Var:
+ if (ep) {
+ *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
} else {
- const QMetaType propType = propertyData->propType();
- const void *data = nullptr;
- if (const QV4::VariantObject *v = sv->as<QV4::VariantObject>()) {
- const QVariant &variant = v->d()->data();
- if (variant.metaType() == propType)
- data = variant.constData();
+ // if the context was disposed,
+ // we just return an invalid variant from read.
+ *reinterpret_cast<QVariant *>(a[0]) = QVariant();
+ }
+ break;
+ case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
+ QV4::Scope scope(engine);
+ QV4::ScopedValue sv(scope, *(md->data() + id));
+
+ // _id because this is an absolute property ID.
+ const QQmlPropertyData *propertyData = cache->property(_id);
+
+ if (propertyData->isQObject()) {
+ if (const auto *wrap = sv->as<QV4::QObjectWrapper>())
+ *reinterpret_cast<QObject **>(a[0]) = wrap->object();
+ else
+ *reinterpret_cast<QObject **>(a[0]) = nullptr;
+ } else {
+ const QMetaType propType = propertyData->propType();
+ const void *data = nullptr;
+ if (const auto *v = sv->as<QV4::VariantObject>()) {
+ const QVariant &variant = v->d()->data();
+ if (variant.metaType() == propType)
+ data = variant.constData();
+ }
+ propType.destruct(a[0]);
+ propType.construct(a[0], data);
}
- propType.destruct(a[0]);
- propType.construct(a[0], data);
+ } else {
+ qmlWarning(object) << "Cannot find member data";
}
- } else {
- qmlWarning(object) << "Cannot find member data";
}
}
-
} else if (c == QMetaObject::WriteProperty) {
bool needActivate = false;
- switch (t) {
- case QV4::CompiledData::BuiltinType::Int:
- needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
- writeProperty(id, *reinterpret_cast<int *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Bool:
- needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
- writeProperty(id, *reinterpret_cast<bool *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Real:
- needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
- writeProperty(id, *reinterpret_cast<double *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::String:
- needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
- writeProperty(id, *reinterpret_cast<QString *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Url:
- needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
- writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Date:
- needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
- writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::DateTime:
- needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
- writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Rect:
- needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
- writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Size:
- needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
- writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Point:
- needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
- writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Time:
- needActivate = *reinterpret_cast<QTime *>(a[0]) != readPropertyAsTime(id);
- writeProperty(id, *reinterpret_cast<QTime *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::Var:
- if (ep)
- writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
- break;
- case QV4::CompiledData::BuiltinType::InvalidBuiltin:
- if (property.isList) {
- // Writing such a property is not supported. Content is added through the list property
- // methods.
- } else if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
- QV4::Scope scope(engine);
- QV4::ScopedValue sv(scope, *(md->data() + id));
- // _id because this is an absolute property ID.
- const QQmlPropertyData *propertyData = cache->property(_id);
+ if (property.isList) {
+ // _id because this is an absolute property ID.
+ const QQmlPropertyData *propertyData = cache->property(_id);
+ const QMetaType propType = propertyData->propType();
- if (propertyData->isQObject()) {
- QObject *arg = *reinterpret_cast<QObject **>(a[0]);
- if (const QV4::QObjectWrapper *wrap = sv->as<QV4::QObjectWrapper>())
- needActivate = wrap->object() != arg;
- else
+ if (propType.flags().testFlag(QMetaType::IsQmlList)) {
+ // Writing such a property is not supported. Content is added through
+ // the list property methods.
+ } else if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
+ // Value type list
+ QV4::Scope scope(engine);
+ QV4::ScopedObject sequence(scope, *(md->data() + id));
+ void *data = sequence
+ ? QV4::SequencePrototype::getRawContainerPtr(sequence, propType)
+ : nullptr;
+ if (data) {
+ if (!propType.equals(data, a[0])) {
+ propType.destruct(data);
+ propType.construct(data, a[0]);
needActivate = true;
- if (needActivate)
- writeProperty(id, arg);
+ }
} else {
- const QMetaType propType = propertyData->propType();
- if (const QV4::VariantObject *v = sv->as<QV4::VariantObject>()) {
- QVariant &variant = v->d()->data();
- if (variant.metaType() != propType) {
+ bool success = false;
+ md->set(engine, id, QV4::SequencePrototype::fromData(
+ engine, propType, a[0], &success));
+ if (!success) {
+ qmlWarning(object)
+ << "Could not create a QML sequence object for"
+ << propType.name();
+ }
+ needActivate = true;
+ }
+ } else {
+ qmlWarning(object) << "Cannot find member data";
+ }
+ } else {
+ switch (t) {
+ case QV4::CompiledData::BuiltinType::Int:
+ needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
+ writeProperty(id, *reinterpret_cast<int *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Bool:
+ needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
+ writeProperty(id, *reinterpret_cast<bool *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Real:
+ needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
+ writeProperty(id, *reinterpret_cast<double *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::String:
+ needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
+ writeProperty(id, *reinterpret_cast<QString *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Url:
+ needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
+ writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Date:
+ needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
+ writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::DateTime:
+ needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
+ writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Rect:
+ needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
+ writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Size:
+ needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
+ writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Point:
+ needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
+ writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Time:
+ needActivate = *reinterpret_cast<QTime *>(a[0]) != readPropertyAsTime(id);
+ writeProperty(id, *reinterpret_cast<QTime *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Var:
+ if (ep)
+ writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
+ QV4::Scope scope(engine);
+ QV4::ScopedValue sv(scope, *(md->data() + id));
+
+ // _id because this is an absolute property ID.
+ const QQmlPropertyData *propertyData = cache->property(_id);
+
+ if (propertyData->isQObject()) {
+ QObject *arg = *reinterpret_cast<QObject **>(a[0]);
+ if (const auto *wrap = sv->as<QV4::QObjectWrapper>())
+ needActivate = wrap->object() != arg;
+ else
needActivate = true;
- variant = QVariant(propType, a[0]);
- } else if (!propType.equals(variant.constData(), a[0])) {
+ if (needActivate)
+ writeProperty(id, arg);
+ } else {
+ const QMetaType propType = propertyData->propType();
+ if (const auto *v = sv->as<QV4::VariantObject>()) {
+ QVariant &variant = v->d()->data();
+ if (variant.metaType() != propType) {
+ needActivate = true;
+ variant = QVariant(propType, a[0]);
+ } else if (!propType.equals(variant.constData(), a[0])) {
+ needActivate = true;
+ propType.destruct(variant.data());
+ propType.construct(variant.data(), a[0]);
+ }
+ } else {
needActivate = true;
- propType.destruct(variant.data());
- propType.construct(variant.data(), a[0]);
+ md->set(engine, id, engine->newVariantObject(
+ QVariant(propType, a[0])));
}
- } else {
- needActivate = true;
- md->set(engine, id, engine->newVariantObject(
- QVariant(propType, a[0])));
}
+ } else {
+ qmlWarning(object) << "Cannot find member data";
}
- } else {
- qmlWarning(object) << "Cannot find member data";
}
}
diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp
index bf74fa9ca2..559b4c71d2 100644
--- a/src/qmltyperegistrar/qmltypescreator.cpp
+++ b/src/qmltyperegistrar/qmltypescreator.cpp
@@ -88,6 +88,12 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (collector.elementName.isEmpty())
return;
+ if (!collector.sequenceValueType.isEmpty()) {
+ qWarning() << "Ignoring name of sequential container:" << collector.elementName;
+ qWarning() << "Sequential containers are anonymous. Use QML_ANONYMOUS to register them.";
+ return;
+ }
+
QStringList exports;
QStringList metaObjects;
diff --git a/src/qmlworkerscript/qv4serialize.cpp b/src/qmlworkerscript/qv4serialize.cpp
index 126ebe5e41..b9b4d6aa08 100644
--- a/src/qmlworkerscript/qv4serialize.cpp
+++ b/src/qmlworkerscript/qv4serialize.cpp
@@ -251,7 +251,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
}
reserve(data, sizeof(quint32) + length * sizeof(quint32));
push(data, valueheader(WorkerSequence, length));
- serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
+
+ // sequence type
+ serialize(data, QV4::Value::fromInt32(
+ QV4::SequencePrototype::metaTypeForSequence(o).id()), engine);
+
ScopedValue val(scope);
for (uint ii = 0; ii < seqLength; ++ii)
serialize(data, (val = o->get(ii)), engine); // sequence elements
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index a52084de13..f7b846e9a3 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -595,6 +595,7 @@ void tst_QJSEngine::toScriptValueQtQml_data()
QTest::newRow("QList<bool>") << QVariant::fromValue(QList<bool>{true, false, true, false});
QTest::newRow("QStringList") << QVariant::fromValue(QStringList{"a", "b", "c", "d"});
QTest::newRow("QList<QUrl>") << QVariant::fromValue(QList<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")});
+ QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
static const QStandardItemModel model(4, 4);
QTest::newRow("QModelIndexList") << QVariant::fromValue(QModelIndexList{ model.index(1, 2), model.index(2, 3), model.index(3, 1), model.index(3, 2)});
@@ -642,8 +643,6 @@ void tst_QJSEngine::toScriptValuenotroundtripped_data()
QTest::newRow("QList<QObject*>") << QVariant::fromValue(QList<QObject*>() << this) << QVariant(QVariantList() << QVariant::fromValue<QObject *>(this));
QTest::newRow("QObjectList") << QVariant::fromValue(QObjectList() << this) << QVariant(QVariantList() << QVariant::fromValue<QObject *>(this));
- QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
- QTest::newRow("QVector<QPoint>") << QVariant::fromValue(QVector<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
QTest::newRow("VoidStar") << QVariant(QMetaType(QMetaType::VoidStar), nullptr) << QVariant(QMetaType(QMetaType::Nullptr), nullptr);
}
diff --git a/tests/auto/qml/qqmllanguage/data/ValueTypeListBase.qml b/tests/auto/qml/qqmllanguage/data/ValueTypeListBase.qml
new file mode 100644
index 0000000000..e30990f884
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/ValueTypeListBase.qml
@@ -0,0 +1,9 @@
+import QtQml
+import ValueTypes
+
+QtObject {
+ property list<int> a
+ property list<point> b
+ property list<derived> x
+ property list<base> baseList
+}
diff --git a/tests/auto/qml/qqmllanguage/data/valueTypeList.qml b/tests/auto/qml/qqmllanguage/data/valueTypeList.qml
new file mode 100644
index 0000000000..ed1c850d00
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/valueTypeList.qml
@@ -0,0 +1,22 @@
+import QtQml
+import ValueTypes
+
+ValueTypeListBase {
+ a: [0, 2, 3, 1]
+ b: [Qt.point(1, 2), Qt.point(3, 4)]
+ property int c: a[2]
+ property point d: b[1]
+ property derived y
+ x: [y, y, y]
+ baseList: [y, y, y]
+
+ Component.onCompleted: {
+ a[2] = 17
+ y.increment()
+ }
+
+ onObjectNameChanged: {
+ x[1].increment()
+ b[1].x = 12
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 87116db4ad..fa8c2da3c8 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -391,6 +391,7 @@ private slots:
void ambiguousContainingType();
void objectAsBroken();
void customValueTypes();
+ void valueTypeList();
private:
QQmlEngine engine;
@@ -6750,6 +6751,49 @@ void tst_qqmllanguage::customValueTypes()
QCOMPARE(qvariant_cast<BaseValueType>(o->property("base")).content(), 13);
}
+void tst_qqmllanguage::valueTypeList()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("valueTypeList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ {
+ QCOMPARE(o->property("c").toInt(), 17);
+ QCOMPARE(qvariant_cast<QPointF>(o->property("d")), QPointF(3, 4));
+ QCOMPARE(qvariant_cast<DerivedValueType>(o->property("y")).content(), 29);
+ const QList<DerivedValueType> x = qvariant_cast<QList<DerivedValueType>>(o->property("x"));
+ QCOMPARE(x.length(), 3);
+ for (const DerivedValueType &d : x)
+ QCOMPARE(d.content(), 29);
+
+ const QList<BaseValueType> baseList
+ = qvariant_cast<QList<BaseValueType>>(o->property("baseList"));
+ QCOMPARE(baseList.length(), 3);
+ for (const BaseValueType &b : baseList)
+ QCOMPARE(b.content(), 29);
+ }
+
+ o->setObjectName(QStringLiteral("foo"));
+ {
+ // See QTBUG-99766
+ QEXPECT_FAIL("", "Write-back for value types is still incomplete", Abort);
+ QCOMPARE(qvariant_cast<QPointF>(o->property("d")), QPointF(12, 4));
+ QCOMPARE(qvariant_cast<DerivedValueType>(o->property("y")).content(), 30);
+ const QList<DerivedValueType> x = qvariant_cast<QList<DerivedValueType>>(o->property("x"));
+ QCOMPARE(x.length(), 3);
+ for (const DerivedValueType &d : x)
+ QCOMPARE(d.content(), 30);
+
+ const QList<BaseValueType> baseList
+ = qvariant_cast<QList<BaseValueType>>(o->property("baseList"));
+ QCOMPARE(baseList.length(), 3);
+ for (const BaseValueType &b : baseList)
+ QCOMPARE(b.content(), 30);
+ }
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"