From 5f3b8bd08c45c4c1d03eab475648a94e02be82aa Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 25 Jan 2023 12:26:44 +0100 Subject: [PATCH] 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 --- src/qml/qml/qqmlproperty.cpp | 95 ++++++++++++------- .../qml/qmlcppcodegen/data/badSequence.qml | 13 +++ .../qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 11 +++ 3 files changed, 87 insertions(+), 32 deletions(-) diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 18e2c417ff..ad08f06b05 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1439,6 +1439,35 @@ static ConvertAndAssignResult tryConvertAndAssign( return {false, false}; }; +template +bool iterateQObjectContainer(QMetaType metaType, const void *data, Op op) +{ + QSequentialIterable iterable; + if (!QMetaType::convert(metaType, data, QMetaType::fromType(), &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 &context, QQmlPropertyData::WriteFlags flags) @@ -1552,29 +1581,7 @@ bool QQmlPropertyPrivate::write( const QList &list = qvariant_cast >(value); for (qsizetype ii = 0; ii < list.size(); ++ii) doAppend(list.at(ii)); - } else if (QSequentialIterable iterable; - QMetaType::convert(variantMetaType, value.data(), - QMetaType::fromType(), &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(), &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()) { const QVariantList list = value.value(); 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(value.data())); + ok = true; + } else if (variantMetaType == QMetaType::fromType()) { + const QQmlListReference *reference + = static_cast(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; } } diff --git a/tests/auto/qml/qmlcppcodegen/data/badSequence.qml b/tests/auto/qml/qmlcppcodegen/data/badSequence.qml index ff8981ec36..ff173b5002 100644 --- a/tests/auto/qml/qmlcppcodegen/data/badSequence.qml +++ b/tests/auto/qml/qmlcppcodegen/data/badSequence.qml @@ -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 others: cousins + + property Person mom: Person { + id: mm + cousins: self.others + } + property list momsCousins: mm.cousins + + property Person dad: Person { + id: dd + cousins: oo + } + property list dadsCousins: dd.cousins } diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index 9b67793ce6..8b2b4bbbf4 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -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 momsCousins + = self->property("momsCousins").value>(); + QCOMPARE(momsCousins.count(&momsCousins), 2); + QCOMPARE(momsCousins.at(&momsCousins, 0), cousins[0]); + QCOMPARE(momsCousins.at(&momsCousins, 1), cousins[1]); + + QQmlListProperty dadsCousins + = self->property("dadsCousins").value>(); + QCOMPARE(dadsCousins.count(&dadsCousins), 1); + QCOMPARE(dadsCousins.at(&dadsCousins, 0), other); } void tst_QmlCppCodegen::enumLookup()