aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-06-01 12:54:23 +0200
committerUlf Hermann <ulf.hermann@qt.io>2023-06-09 11:49:35 +0200
commit052c15ae8349fd757e9a042012697021c40ead96 (patch)
treef9d75d4c6943687e56d5ab78c94a4a979588f661
parent56a4c899225db8b8badc50a2553e1352c06893f0 (diff)
Qml: Construct value types by properties from any object-likes
Since we allow any object to be used for property-by-property construction when encoded as QJSValue, we should do the same when it's encoded as QVariant. This allows us to use typed modelData in delegates. The modelData's type only has to be a structured value. Any matching data from the model will then be inserted. Fixes: QTBUG-113752 Change-Id: I200e9acac3c6c302c9dedf8e7e1ccd6c9fdf5eb6 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 062843124470e90ada7323b0734ff35addb0a9cb)
-rw-r--r--src/qml/qml/qqmlglobal.cpp353
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml50
-rw-r--r--tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp70
3 files changed, 380 insertions, 93 deletions
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 8653ef0783..3c8edeb425 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -71,30 +71,32 @@ static bool isConstructibleMetaType(const QMetaType metaType)
}
static void callConstructor(
- const QMetaObject *mo, int i, void *parameter, QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, int i, QMetaType targetMetaType, void *target,
+ void *source)
{
// Unfortunately CreateInstance unconditionally creates the instance on the heap.
void *gadget = nullptr;
- void *p[] = { &gadget, parameter };
- mo->static_metacall(QMetaObject::CreateInstance, i, p);
+ void *p[] = { &gadget, source };
+ targetMetaObject->static_metacall(QMetaObject::CreateInstance, i, p);
Q_ASSERT(gadget);
- metaType.destruct(data);
- metaType.construct(data, gadget);
- metaType.destroy(gadget);
+ targetMetaType.destruct(target);
+ targetMetaType.construct(target, gadget);
+ targetMetaType.destroy(gadget);
}
static bool fromMatchingType(
- const QMetaObject *mo, const QV4::Value &s, const QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, const QMetaType targetMetaType, void *target,
+ const QV4::Value &source)
{
- for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
- const QMetaMethod ctor = mo->constructor(i);
+ for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) {
+ const QMetaMethod ctor = targetMetaObject->constructor(i);
if (ctor.parameterCount() != 1)
continue;
const QMetaType parameterType = ctor.parameterMetaType(0);
- QVariant parameter = QV4::ExecutionEngine::toVariant(s, parameterType);
+ QVariant parameter = QV4::ExecutionEngine::toVariant(source, parameterType);
if (parameter.metaType() == parameterType) {
- callConstructor(mo, i, parameter.data(), metaType, data);
+ callConstructor(targetMetaObject, i, targetMetaType, target, parameter.data());
return true;
}
@@ -105,7 +107,7 @@ static bool fromMatchingType(
QVariant converted(parameterType);
if (QMetaType::convert(parameter.metaType(), parameter.constData(),
parameterType, converted.data())) {
- callConstructor(mo, i, converted.data(), metaType, data);
+ callConstructor(targetMetaObject, i, targetMetaType, target, converted.data());
return true;
}
}
@@ -114,23 +116,25 @@ static bool fromMatchingType(
}
static bool fromMatchingType(
- const QMetaObject *mo, QVariant s, const QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, const QMetaType targetMetaType, void *target,
+ QVariant source)
{
- const QMetaType sourceMetaType = s.metaType();
+ const QMetaType sourceMetaType = source.metaType();
if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
- QJSValue val = s.value<QJSValue>();
+ QJSValue val = source.value<QJSValue>();
return fromMatchingType(
- mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), metaType, data);
+ targetMetaObject, targetMetaType, target,
+ QV4::Value(QJSValuePrivate::asReturnedValue(&val)));
}
- for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
- const QMetaMethod ctor = mo->constructor(i);
+ for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) {
+ const QMetaMethod ctor = targetMetaObject->constructor(i);
if (ctor.parameterCount() != 1)
continue;
const QMetaType parameterType = ctor.parameterMetaType(0);
if (sourceMetaType == parameterType) {
- callConstructor(mo, i, s.data(), metaType, data);
+ callConstructor(targetMetaObject, i, targetMetaType, target, source.data());
return true;
}
@@ -139,8 +143,9 @@ static bool fromMatchingType(
// At this point, s should be a builtin type. For builtin types
// the QMetaType converters are good enough.
QVariant parameter(parameterType);
- if (QMetaType::convert(sourceMetaType, s.constData(), parameterType, parameter.data())) {
- callConstructor(mo, i, parameter.data(), metaType, data);
+ if (QMetaType::convert(
+ sourceMetaType, source.constData(), parameterType, parameter.data())) {
+ callConstructor(targetMetaObject, i, targetMetaType, target, parameter.data());
return true;
}
}
@@ -149,97 +154,254 @@ static bool fromMatchingType(
}
static bool fromString(
- const QMetaObject *mo, QString s, const QMetaType metaType, void *data)
+ const QMetaObject *targetMetaObject, const QMetaType targetMetaType, void *target,
+ QString source)
{
- for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
- const QMetaMethod ctor = mo->constructor(i);
+ for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) {
+ const QMetaMethod ctor = targetMetaObject->constructor(i);
if (ctor.parameterCount() != 1)
continue;
if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) {
- callConstructor(mo, i, &s, metaType, data);
+ callConstructor(targetMetaObject, i, targetMetaType, target, &source);
return true;
}
}
-
return false;
}
-static bool byProperties(
- const QMetaObject *mo, const QV4::Value &s, void *data)
+template<typename Get, typename Convert>
+static bool doWriteProperty(const QMetaProperty &metaProperty, void *target,
+ Get &&get, Convert &&convert)
{
- if (!s.isObject())
- return false;
-
- if (!mo)
- return false;
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = get(propertyType);
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ return true;
+ }
+ QVariant converted = convert(propertyType);
+ if (converted.isValid()) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ return true;
+ }
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ return true;
+ }
+ return false;
+}
- const QV4::Object *o = static_cast<const QV4::Object *>(&s);
+static void doWriteProperties(
+ const QMetaObject *targetMetaObject, void *target, const QV4::Value &source)
+{
+ const QV4::Object *o = static_cast<const QV4::Object *>(&source);
QV4::Scope scope(o->engine());
QV4::ScopedObject object(scope, o);
-
- for (int i = 0; i < mo->propertyCount(); ++i) {
- const QMetaProperty metaProperty = mo->property(i);
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
const QString propertyName = QString::fromUtf8(metaProperty.name());
-
QV4::ScopedString v4PropName(scope, scope.engine->newString(propertyName));
QV4::ScopedValue v4PropValue(scope, object->get(v4PropName));
-
// We assume that data is freshly constructed.
// There is no point in reset()'ing properties of a freshly created object.
if (v4PropValue->isUndefined())
continue;
-
+ if (doWriteProperty(metaProperty, target, [&](const QMetaType &propertyType) {
+ return QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
+ }, [&](const QMetaType &propertyType) {
+ QVariant result(propertyType);
+ return QQmlValueTypeProvider::createValueType(
+ v4PropValue, propertyType, result.data())
+ ? result
+ : QVariant();
+ })) {
+ continue;
+ }
const QMetaType propertyType = metaProperty.metaType();
QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
if (property.metaType() == propertyType) {
- metaProperty.writeOnGadget(data, property);
+ metaProperty.writeOnGadget(target, std::move(property));
continue;
}
QVariant converted(propertyType);
if (QQmlValueTypeProvider::createValueType(v4PropValue, propertyType, converted.data())) {
- metaProperty.writeOnGadget(data, converted);
+ metaProperty.writeOnGadget(target, std::move(converted));
continue;
}
-
+ converted = QVariant(propertyType);
if (QMetaType::convert(property.metaType(), property.constData(),
propertyType, converted.data())) {
- metaProperty.writeOnGadget(data, converted);
+ metaProperty.writeOnGadget(target, std::move(converted));
continue;
}
-
qWarning().noquote()
<< QLatin1String("Could not convert %1 to %2 for property %3")
.arg(v4PropValue->toQStringNoThrow(), QString::fromUtf8(propertyType.name()),
propertyName);
}
+}
+
+static bool byProperties(
+ const QMetaObject *targetMetaObject, void *target, const QV4::Value &source)
+{
+ if (!source.isObject() || !targetMetaObject)
+ return false;
+
+ doWriteProperties(targetMetaObject, target, source);
+ return true;
+}
+
+template<typename Read>
+static void doWriteProperties(
+ const QMetaObject *targetMetaObject, void *target,
+ const QMetaObject *sourceMetaObject, Read &&read)
+{
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
+ const int sourceProperty = sourceMetaObject->indexOfProperty(metaProperty.name());
+ // We assume that data is freshly constructed.
+ // There is no point in reset()'ing properties of a freshly created object.
+ if (sourceProperty == -1)
+ continue;
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = read(sourceMetaObject, sourceProperty);
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ continue;
+ }
+ QVariant converted(propertyType);
+ if (QQmlValueTypeProvider::createValueType(property, propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+ qWarning().noquote()
+ << QLatin1String("Could not convert %1 to %2 for property %3")
+ .arg(property.toString(), QString::fromUtf8(propertyType.name()),
+ QString::fromUtf8(metaProperty.name()));
+ }
+}
+
+static void doWriteProperties(const QMetaObject *targetMeta, void *target, QObject *source)
+{
+ doWriteProperties(
+ targetMeta, target, source->metaObject(),
+ [source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).read(source);
+ });
+}
+
+static bool byProperties(const QMetaObject *targetMetaObject, void *target, QObject *source)
+{
+ if (!source || !targetMetaObject)
+ return false;
+ doWriteProperties(targetMetaObject, target, source);
return true;
}
static bool byProperties(
- const QMetaObject *mo, const QVariant &s, void *data)
+ const QMetaObject *targetMetaObject, void *target,
+ const QMetaObject *sourceMetaObject, const void *source)
{
- if (!mo)
+ if (!source || !sourceMetaObject || !targetMetaObject)
return false;
+ doWriteProperties(
+ targetMetaObject, target, sourceMetaObject,
+ [source](const QMetaObject *sourceMetaObject, int sourceProperty) {
+ return sourceMetaObject->property(sourceProperty).readOnGadget(source);
+ });
+ return true;
+}
- if (s.metaType() == QMetaType::fromType<QJSValue>()) {
- QJSValue val = s.value<QJSValue>();
- return byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), data);
+template<typename Map>
+void doWriteProperties(const QMetaObject *targetMetaObject, void *target, const Map &source)
+{
+ for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
+ const QMetaProperty metaProperty = targetMetaObject->property(i);
+ // We assume that data is freshly constructed.
+ // There is no point in reset()'ing properties of a freshly created object.
+ const auto it = source.constFind(QString::fromUtf8(metaProperty.name()));
+ if (it == source.constEnd())
+ continue;
+ const QMetaType propertyType = metaProperty.metaType();
+ QVariant property = *it;
+ if (property.metaType() == propertyType) {
+ metaProperty.writeOnGadget(target, std::move(property));
+ continue;
+ }
+ QVariant converted(propertyType);
+ if (QQmlValueTypeProvider::createValueType(property, propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+ converted = QVariant(propertyType);
+ if (QMetaType::convert(property.metaType(), property.constData(),
+ propertyType, converted.data())) {
+ metaProperty.writeOnGadget(target, std::move(converted));
+ continue;
+ }
+ qWarning().noquote()
+ << QLatin1String("Could not convert %1 to %2 for property %3")
+ .arg(property.toString(), QString::fromUtf8(propertyType.name()),
+ QString::fromUtf8(metaProperty.name()));
+ }
+}
+template<typename Map>
+bool byProperties(
+ const QMetaObject *targetMetaObject, void *target, const Map &source)
+{
+ doWriteProperties(targetMetaObject, target, source);
+ return true;
+}
+
+static bool byProperties(
+ const QMetaObject *targetMetaObject, void *target, const QVariant &source)
+{
+ if (!targetMetaObject)
+ return false;
+
+ if (source.metaType() == QMetaType::fromType<QJSValue>()) {
+ QJSValue val = source.value<QJSValue>();
+ return byProperties(
+ targetMetaObject, target, QV4::Value(QJSValuePrivate::asReturnedValue(&val)));
+ }
+
+ if (source.metaType() == QMetaType::fromType<QVariantMap>()) {
+ return byProperties(
+ targetMetaObject, target,
+ *static_cast<const QVariantMap *>(source.constData()));
+ }
+ if (source.metaType() == QMetaType::fromType<QVariantHash>()) {
+ return byProperties(
+ targetMetaObject, target,
+ *static_cast<const QVariantHash *>(source.constData()));
}
+ if (source.metaType().flags() & QMetaType::PointerToQObject)
+ return byProperties(targetMetaObject, target, source.value<QObject *>());
+ if (const QMetaObject *sourceMeta = QQmlMetaType::metaObjectForValueType(source.metaType()))
+ return byProperties(targetMetaObject, target, sourceMeta, source.constData());
return false;
}
static bool fromJSValue(
- const QQmlType &type, const QJSValue &s, QMetaType metaType, void *data)
+ const QQmlType &targetType, QMetaType targetMetaType, void *target, const QJSValue &source)
{
- if (const auto valueTypeFunction = type.createValueTypeFunction()) {
- QVariant result = valueTypeFunction(s);
- if (result.metaType() == metaType) {
- metaType.destruct(data);
- metaType.construct(data, result.constData());
+ if (const auto valueTypeFunction = targetType.createValueTypeFunction()) {
+ QVariant result = valueTypeFunction(source);
+ if (result.metaType() == targetMetaType) {
+ targetMetaType.destruct(target);
+ targetMetaType.construct(target, result.constData());
return true;
}
}
@@ -248,69 +410,74 @@ static bool fromJSValue(
}
bool QQmlValueTypeProvider::constructFromJSValue(
- const QJSValue &s, QMetaType metaType, void *data)
+ const QJSValue &source, QMetaType targetMetaType, void *target)
{
- return isConstructibleMetaType(metaType)
- && fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType, data);
+ return isConstructibleMetaType(targetMetaType)
+ && fromJSValue(QQmlMetaType::qmlType(targetMetaType), targetMetaType, target, source);
}
bool QQmlValueTypeProvider::createValueType(
- const QString &s, QMetaType metaType, void *data)
+ const QString &source, QMetaType targetMetaType, void *target)
{
- if (!isConstructibleMetaType(metaType))
+ if (!isConstructibleMetaType(targetMetaType))
return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type);
- if (mo && type.canConstructValueType()) {
- if (fromString(mo, s, metaType, data))
+ const QQmlType targetType = QQmlMetaType::qmlType(targetMetaType);
+ const QMetaObject *targetMetaObject = QQmlMetaType::metaObjectForValueType(targetType);
+ if (targetMetaObject && targetType.canConstructValueType()) {
+ if (fromString(targetMetaObject, targetMetaType, target, source))
return true;
}
- return fromJSValue(type, s, metaType, data);
+ return fromJSValue(targetType, targetMetaType, target, source);
}
bool QQmlValueTypeProvider::createValueType(
- const QJSValue &s, QMetaType metaType, void *data)
+ const QJSValue &source, QMetaType targetMetaType, void *target)
{
- if (!isConstructibleMetaType(metaType))
+ if (!isConstructibleMetaType(targetMetaType))
return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType()
- && byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)), data)) {
+ const QQmlType targetType = QQmlMetaType::qmlType(targetMetaType);
+ if (const QMetaObject *targetMetaObject = QQmlMetaType::metaObjectForValueType(targetType)) {
+ if (targetType.canPopulateValueType()
+ && byProperties(
+ targetMetaObject, target,
+ QV4::Value(QJSValuePrivate::asReturnedValue(&source)))) {
return true;
}
- if (type.canConstructValueType()
- && fromMatchingType(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)),
- metaType, data)) {
+ if (targetType.canConstructValueType()
+ && fromMatchingType(
+ targetMetaObject, targetMetaType, target,
+ QV4::Value(QJSValuePrivate::asReturnedValue(&source)))) {
return true;
}
}
- return fromJSValue(type, s, metaType, data);
+ return fromJSValue(targetType, targetMetaType, target, source);
}
bool QQmlValueTypeProvider::createValueType(
- const QV4::Value &s, QMetaType metaType, void *data)
+ const QV4::Value &source, QMetaType targetMetaType, void *target)
{
- if (!isConstructibleMetaType(metaType))
+ if (!isConstructibleMetaType(targetMetaType))
return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType() && byProperties(mo, s, data))
+ const QQmlType targetType = QQmlMetaType::qmlType(targetMetaType);
+ if (const QMetaObject *targetMetaObject = QQmlMetaType::metaObjectForValueType(targetType)) {
+ if (targetType.canPopulateValueType() && byProperties(targetMetaObject, target, source))
return true;
- if (type.canConstructValueType()) {
- if (fromMatchingType(mo, s, metaType, data))
+ if (targetType.canConstructValueType()) {
+ if (fromMatchingType(targetMetaObject, targetMetaType, target, source))
return true;
qWarning().noquote()
<< "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s.toQStringNoThrow();
+ << targetMetaObject->className() << "to call with value"
+ << source.toQStringNoThrow();
}
}
return fromJSValue(
- type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType, data);
+ targetType, targetMetaType, target,
+ QJSValuePrivate::fromReturnedValue(source.asReturnedValue()));
}
@@ -319,20 +486,20 @@ bool QQmlValueTypeProvider::createValueType(
* This should only be called with either builtin types or wrapped QJSValues as source.
*/
bool QQmlValueTypeProvider::createValueType(
- const QVariant &s, QMetaType metaType, void *data)
+ const QVariant &source, QMetaType targetMetaType, void *target)
{
- if (!isConstructibleMetaType(metaType))
+ if (!isConstructibleMetaType(targetMetaType))
return false;
- const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- if (type.canPopulateValueType() && byProperties(mo, s, data))
+ const QQmlType targetType = QQmlMetaType::qmlType(targetMetaType);
+ if (const QMetaObject *targetMeaObject = QQmlMetaType::metaObjectForValueType(targetType)) {
+ if (targetType.canPopulateValueType() && byProperties(targetMeaObject, target, source))
return true;
- if (type.canConstructValueType()) {
- if (fromMatchingType(mo, s, metaType, data))
+ if (targetType.canConstructValueType()) {
+ if (fromMatchingType(targetMeaObject, targetMetaType, target, source))
return true;
qWarning().noquote()
<< "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s;
+ << targetMeaObject->className() << "to call with value" << source;
}
}
diff --git a/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml b/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml
new file mode 100644
index 0000000000..b24bf4f6b3
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/typedModelData.qml
@@ -0,0 +1,50 @@
+import QtQml
+
+DelegateModel {
+ id: root
+
+ // useful object as model, int as modelData
+ property ListModel singularModel: ListModel {
+ ListElement {
+ x: 11
+ }
+ ListElement {
+ x: 12
+ }
+ }
+
+ // useful but different objects as modelData and model
+ // This is how the array accessor works. We can live with it.
+ property var array: [
+ {x: 17, y: 18}, {x: 19, y: 20}
+ ]
+
+ // useful but different objects as modelData and model
+ // This is how the object accessor works. We can live with it.
+ property QtObject object: QtObject {
+ property int x: 21
+ property int y: 22
+ }
+
+ property int n: -1
+
+ model: {
+ switch (n) {
+ case 0: return singularModel
+ case 1: return array
+ case 2: return object
+ }
+ return undefined;
+ }
+
+ delegate: QtObject {
+ required property point modelData
+ required property QtObject model
+
+ property real modelX: model.x
+ property real modelDataX: modelData.x
+ property point modelSelf: model
+ property point modelDataSelf: modelData
+ property point modelModelData: model.modelData
+ }
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
index 865a3719d0..8b433d346c 100644
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
@@ -26,6 +26,7 @@ private slots:
void contextAccessedByHandler();
void redrawUponColumnChange();
void nestedDelegates();
+ void typedModelData();
void deleteRace();
};
@@ -208,6 +209,75 @@ void tst_QQmlDelegateModel::nestedDelegates()
QFAIL("Loader not found");
}
+void tst_QQmlDelegateModel::typedModelData()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("typedModelData.qml");
+ QQmlComponent c(&engine, url);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(o.data());
+ QVERIFY(delegateModel);
+
+ for (int i = 0; i < 3; ++i) {
+ if (i == 0) {
+ for (int j = 0; j < 2; ++j) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Could not find any constructor for value type QQmlPointFValueType "
+ "to call with value QVariant(double, 11)");
+ }
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + ":48:9: Unable to assign double to QPointF"));
+ } else if (i == 1) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(url.toString() + ":44:9: Unable to assign [undefined] to double"));
+ }
+
+ delegateModel->setProperty("n", i);
+ QObject *delegate = delegateModel->object(0);
+ QVERIFY(delegate);
+ switch (i) {
+ case 0: {
+ // list model with 1 role.
+ // Does not work, for the most part, because the model is singular
+ QCOMPARE(delegate->property("modelX"), 11.0);
+ QCOMPARE(delegate->property("modelDataX"), 0.0);
+ QCOMPARE(delegate->property("modelSelf"), QPointF(11.0, 0.0));
+ QCOMPARE(delegate->property("modelDataSelf"), QPointF());
+ QCOMPARE(delegate->property("modelModelData"), QPointF());
+ break;
+ }
+ case 1: {
+ // JS array of objects
+ QCOMPARE(delegate->property("modelDataX"), 17.0);
+ const QPointF modelData = delegate->property("modelDataSelf").value<QPointF>();
+ QCOMPARE(modelData, QPointF(17, 18));
+ QCOMPARE(delegate->property("modelModelData").value<QPointF>(), modelData);
+ break;
+ }
+ case 2: {
+ // single object
+ QCOMPARE(delegate->property("modelX"), 21);
+ QCOMPARE(delegate->property("modelDataX"), 21);
+ const QPointF modelData = delegate->property("modelDataSelf").value<QPointF>();
+ QCOMPARE(modelData, QPointF(21, 22));
+ QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelData));
+ QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelData));
+ break;
+ }
+ default:
+ QFAIL("wrong model number");
+ break;
+ }
+ }
+
+}
+
void tst_QQmlDelegateModel::deleteRace()
{
QQmlEngine engine;