QML: Allow more conversions between different lists

You should be able to assign any list of QObjects to any other list of
QObjects.

Pick-to: 6.5
Fixes: QTBUG-108155
Change-Id: I6ddf0b49f7248ad56cc9560d217f3ea316c648a8
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
This commit is contained in:
Ulf Hermann 2023-01-25 12:26:44 +01:00
parent 5c496ad952
commit 5f3b8bd08c
3 changed files with 87 additions and 32 deletions

View File

@ -1439,6 +1439,35 @@ static ConvertAndAssignResult tryConvertAndAssign(
return {false, false};
};
template<typename Op>
bool iterateQObjectContainer(QMetaType metaType, const void *data, Op op)
{
QSequentialIterable iterable;
if (!QMetaType::convert(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable))
return false;
const QMetaSequence metaSequence = iterable.metaContainer();
if (!metaSequence.hasConstIterator()
|| !metaSequence.canGetValueAtConstIterator()
|| !iterable.valueMetaType().flags().testFlag(QMetaType::PointerToQObject)) {
return false;
}
const void *container = iterable.constIterable();
void *it = metaSequence.constBegin(container);
const void *end = metaSequence.constEnd(container);
QObject *o = nullptr;
while (!metaSequence.compareConstIterator(it, end)) {
metaSequence.valueAtConstIterator(it, &o);
op(o);
metaSequence.advanceConstIterator(it, 1);
}
metaSequence.destroyConstIterator(it);
metaSequence.destroyConstIterator(end);
return true;
}
bool QQmlPropertyPrivate::write(
QObject *object, const QQmlPropertyData &property, const QVariant &value,
const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
@ -1552,29 +1581,7 @@ bool QQmlPropertyPrivate::write(
const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
for (qsizetype ii = 0; ii < list.size(); ++ii)
doAppend(list.at(ii));
} else if (QSequentialIterable iterable;
QMetaType::convert(variantMetaType, value.data(),
QMetaType::fromType<QSequentialIterable>(), &iterable)) {
const QMetaSequence metaContainer = iterable.metaContainer();
if (metaContainer.hasConstIterator()
&& metaContainer.canGetValueAtConstIterator()
&& iterable.valueMetaType().flags().testFlag(
QMetaType::PointerToQObject)) {
const void *container = iterable.constIterable();
void *it = metaContainer.constBegin(container);
const void *end = metaContainer.constEnd(container);
QObject *o = nullptr;
while (!metaContainer.compareConstIterator(it, end)) {
metaContainer.valueAtConstIterator(it, &o);
doAppend(o);
metaContainer.advanceConstIterator(it, 1);
}
metaContainer.destroyConstIterator(it);
metaContainer.destroyConstIterator(end);
} else {
doAppend(QQmlMetaType::toQObject(value));
}
} else {
} else if (!iterateQObjectContainer(variantMetaType, value.data(), doAppend)) {
doAppend(QQmlMetaType::toQObject(value));
}
} else if (variantMetaType == propertyMetaType) {
@ -1628,33 +1635,57 @@ bool QQmlPropertyPrivate::write(
propertyMetaType, v.data(),
QMetaType::fromType<QSequentialIterable>(),
&iterable)) {
const QMetaSequence metaContainer = iterable.metaContainer();
if (metaContainer.canAddValueAtEnd()) {
const QMetaSequence propertyMetaSequence = iterable.metaContainer();
if (propertyMetaSequence.canAddValueAtEnd()) {
const QMetaType elementMetaType = iterable.valueMetaType();
void *container = iterable.mutableIterable();
void *propertyContainer = iterable.mutableIterable();
if (variantMetaType == elementMetaType) {
metaContainer.addValueAtEnd(container, value.constData());
propertyMetaSequence.addValueAtEnd(propertyContainer, value.constData());
ok = true;
} else if (variantMetaType == QMetaType::fromType<QVariantList>()) {
const QVariantList list = value.value<QVariantList>();
for (const QVariant &valueElement : list) {
if (valueElement.metaType() == elementMetaType) {
metaContainer.addValueAtEnd(
container, valueElement.constData());
propertyMetaSequence.addValueAtEnd(
propertyContainer, valueElement.constData());
} else {
QVariant converted(elementMetaType);
QMetaType::convert(
valueElement.metaType(), valueElement.constData(),
elementMetaType, converted.data());
metaContainer.addValueAtEnd(
container, converted.constData());
propertyMetaSequence.addValueAtEnd(
propertyContainer, converted.constData());
}
}
ok = true;
} else if (elementMetaType.flags().testFlag(QMetaType::PointerToQObject)) {
const QMetaObject *elementMetaObject = elementMetaType.metaObject();
Q_ASSERT(elementMetaObject);
const auto doAppend = [&](QObject *o) {
QObject *casted = elementMetaObject->cast(o);
propertyMetaSequence.addValueAtEnd(propertyContainer, &casted);
};
if (variantMetaType.flags().testFlag(QMetaType::PointerToQObject)) {
doAppend(*static_cast<QObject *const *>(value.data()));
ok = true;
} else if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
const QQmlListReference *reference
= static_cast<const QQmlListReference *>(value.constData());
Q_ASSERT(elementMetaObject);
for (int i = 0, end = reference->size(); i < end; ++i)
doAppend(reference->at(i));
ok = true;
} else if (!iterateQObjectContainer(
variantMetaType, value.data(), doAppend)) {
doAppend(QQmlMetaType::toQObject(value));
}
} else {
QVariant converted = value;
if (converted.convert(elementMetaType)) {
metaContainer.addValueAtEnd(container, converted.constData());
propertyMetaSequence.addValueAtEnd(propertyContainer, converted.constData());
ok = true;
}
}

View File

@ -1,6 +1,7 @@
import TestTypes
Person {
id: self
property Person other: Person { id: oo }
barzles: oo.barzles
cousins: oo.cousins
@ -8,4 +9,16 @@ Person {
property int m: oo.cousins.length
property list<Person> others: cousins
property Person mom: Person {
id: mm
cousins: self.others
}
property list<Person> momsCousins: mm.cousins
property Person dad: Person {
id: dd
cousins: oo
}
property list<Person> dadsCousins: dd.cousins
}

View File

@ -2488,6 +2488,17 @@ void tst_QmlCppCodegen::badSequence()
QCOMPARE(others.count(&others), 2);
QCOMPARE(others.at(&others, 0), cousins[0]);
QCOMPARE(others.at(&others, 1), cousins[1]);
QQmlListProperty<Person> momsCousins
= self->property("momsCousins").value<QQmlListProperty<Person>>();
QCOMPARE(momsCousins.count(&momsCousins), 2);
QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]);
QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]);
QQmlListProperty<Person> dadsCousins
= self->property("dadsCousins").value<QQmlListProperty<Person>>();
QCOMPARE(dadsCousins.count(&dadsCousins), 1);
QCOMPARE(dadsCousins.at(&dadsCousins, 0), other);
}
void tst_QmlCppCodegen::enumLookup()