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>
This commit is contained in:
parent
8c4c0605b0
commit
b0fc028cb5
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
RegisterTypeAndRevisions type = {
|
||||
3,
|
||||
QmlMetaType<T>::self(),
|
||||
QmlMetaType<T>::list(),
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
|
||||
uri,
|
||||
QTypeRevision::fromMajorVersion(versionMajor),
|
||||
uri,
|
||||
QTypeRevision::fromMajorVersion(versionMajor),
|
||||
|
||||
&QQmlTypeNotAvailable::staticMetaObject,
|
||||
classInfoMetaObject,
|
||||
&QQmlTypeNotAvailable::staticMetaObject,
|
||||
classInfoMetaObject,
|
||||
|
||||
attachedPropertiesFunc<T>(),
|
||||
attachedPropertiesMetaObject<T>(),
|
||||
attachedPropertiesFunc<T>(),
|
||||
attachedPropertiesMetaObject<T>(),
|
||||
|
||||
StaticCastSelector<T, QQmlParserStatus>::cast(),
|
||||
StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
|
||||
StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
|
||||
StaticCastSelector<T, QQmlParserStatus>::cast(),
|
||||
StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
|
||||
StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
|
||||
|
||||
nullptr,
|
||||
extension,
|
||||
qmlCreateCustomParser<T>,
|
||||
qmlTypeIds,
|
||||
QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
|
||||
false };
|
||||
nullptr,
|
||||
extension,
|
||||
qmlCreateCustomParser<T>,
|
||||
qmlTypeIds,
|
||||
QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
|
||||
false,
|
||||
QmlMetaType<T>::sequence(),
|
||||
};
|
||||
|
||||
qmlregister(TypeAndRevisionsRegistration, &type);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
QQmlPrivate::RegisterTypeAndRevisions type = {
|
||||
3,
|
||||
QMetaType(),
|
||||
QMetaType(),
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
|
||||
uri,
|
||||
QTypeRevision::fromMajorVersion(versionMajor),
|
||||
uri,
|
||||
QTypeRevision::fromMajorVersion(versionMajor),
|
||||
|
||||
metaObject,
|
||||
(classInfoMetaObject ? classInfoMetaObject
|
||||
: metaObject),
|
||||
metaObject,
|
||||
(classInfoMetaObject ? classInfoMetaObject : metaObject),
|
||||
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
|
||||
nullptr,
|
||||
extensionMetaObject,
|
||||
nullptr,
|
||||
extensionMetaObject,
|
||||
|
||||
&qmlCreateCustomParser<void>,
|
||||
qmlTypeIds,
|
||||
-1,
|
||||
false };
|
||||
&qmlCreateCustomParser<void>,
|
||||
qmlTypeIds,
|
||||
-1,
|
||||
false,
|
||||
QMetaSequence()
|
||||
};
|
||||
|
||||
qmlregister(QQmlPrivate::TypeAndRevisionsRegistration, &type);
|
||||
}
|
||||
|
|
|
@ -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>>()) {
|
||||
|
|
|
@ -82,7 +82,7 @@ public:
|
|||
{
|
||||
if (!m_elementType) {
|
||||
m_elementType = QQmlMetaType::rawMetaObjectForType(
|
||||
QQmlMetaType::listType(propertyType)).metaObject();
|
||||
QQmlMetaType::listValueType(propertyType)).metaObject();
|
||||
}
|
||||
|
||||
return m_elementType;
|
||||
|
|
|
@ -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)
|
||||
QMetaType QQmlMetaType::listValueType(QMetaType metaType)
|
||||
{
|
||||
if (!isList(metaType))
|
||||
return QMetaType {};
|
||||
const auto iface = metaType.iface();
|
||||
if (iface->metaObjectFn == &dynamicQmlListMarker)
|
||||
return QMetaType(static_cast<const QQmlListMetaTypeInterface *>(iface)->valueType);
|
||||
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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1462,43 +1462,54 @@ bool QQmlPropertyPrivate::write(
|
|||
: urlSequence(value);
|
||||
return property.writeProperty(object, &urlSeq, flags);
|
||||
} else if (property.isQList()) {
|
||||
QQmlMetaObject listType;
|
||||
if (propertyMetaType.flags() & QMetaType::IsQmlList) {
|
||||
QMetaType listValueType = QQmlMetaType::listValueType(propertyMetaType);
|
||||
QQmlMetaObject valueMetaObject = QQmlMetaType::rawMetaObjectForType(listValueType);
|
||||
if (valueMetaObject.isNull())
|
||||
return false;
|
||||
|
||||
listType = QQmlMetaType::rawMetaObjectForType(QQmlMetaType::listType(propertyMetaType));
|
||||
if (listType.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, 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 < 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 < 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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -132,6 +132,7 @@ public:
|
|||
|
||||
QMetaType typeId() const;
|
||||
QMetaType qListTypeId() const;
|
||||
QMetaSequence listMetaSequence() const;
|
||||
|
||||
const QMetaObject *metaObject() const;
|
||||
const QMetaObject *baseMetaObject() const;
|
||||
|
|
|
@ -492,7 +492,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
|
|||
const QString propertyName = stringAt(binding->propertyNameIndex);
|
||||
bool notInRevision = false;
|
||||
QQmlPropertyData *pd = resolver.property(propertyName, ¬InRevision);
|
||||
if (!pd)
|
||||
if (!pd || pd->isQList())
|
||||
continue;
|
||||
|
||||
if (!pd->isEnum() && pd->propType().id() != QMetaType::Int)
|
||||
|
|
|
@ -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 if (c == QMetaObject::WriteProperty) {
|
||||
bool needActivate = false;
|
||||
|
||||
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)) {
|
||||
// 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;
|
||||
}
|
||||
} else {
|
||||
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));
|
||||
|
||||
} 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);
|
||||
|
||||
// _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 QV4::QObjectWrapper *wrap = sv->as<QV4::QObjectWrapper>())
|
||||
needActivate = wrap->object() != arg;
|
||||
else
|
||||
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) {
|
||||
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])) {
|
||||
needActivate = true;
|
||||
propType.destruct(variant.data());
|
||||
propType.construct(variant.data(), a[0]);
|
||||
}
|
||||
if (needActivate)
|
||||
writeProperty(id, arg);
|
||||
} else {
|
||||
needActivate = true;
|
||||
md->set(engine, id, engine->newVariantObject(
|
||||
QVariant(propType, a[0])));
|
||||
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;
|
||||
md->set(engine, id, engine->newVariantObject(
|
||||
QVariant(propType, a[0])));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qmlWarning(object) << "Cannot find member data";
|
||||
}
|
||||
} else {
|
||||
qmlWarning(object) << "Cannot find member data";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue