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 Pick-to: 6.5 6.6 Change-Id: I200e9acac3c6c302c9dedf8e7e1ccd6c9fdf5eb6 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
9136e5748b
commit
0628431244
|
@ -2736,7 +2736,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
|
||||||
*reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value);
|
*reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value);
|
||||||
return true;
|
return true;
|
||||||
} else if (!isPointer) {
|
} else if (!isPointer) {
|
||||||
if (QQmlValueTypeProvider::createValueType(value, metaType, data))
|
if (QQmlValueTypeProvider::createValueType(metaType, data, value))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,36 +87,36 @@ static void *createVariantData(QMetaType type, QVariant *variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void callConstructor(
|
static void callConstructor(
|
||||||
const QMetaObject *mo, int i, void *parameter, void *data)
|
const QMetaObject *targetMetaObject, int i, void *source, void *target)
|
||||||
{
|
{
|
||||||
void *p[] = { data, parameter };
|
void *p[] = { target, source };
|
||||||
mo->static_metacall(QMetaObject::ConstructInPlace, i, p);
|
targetMetaObject->static_metacall(QMetaObject::ConstructInPlace, i, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Allocate>
|
template<typename Allocate>
|
||||||
static void fromVerifiedType(
|
static void fromVerifiedType(
|
||||||
const QMetaObject *mo, int ctorIndex, void *sData, Allocate &&allocate)
|
const QMetaObject *targetMetaObject, int ctorIndex, void *source, Allocate &&allocate)
|
||||||
{
|
{
|
||||||
const QMetaMethod ctor = mo->constructor(ctorIndex);
|
const QMetaMethod ctor = targetMetaObject->constructor(ctorIndex);
|
||||||
Q_ASSERT_X(ctor.parameterCount() == 1, "fromVerifiedType",
|
Q_ASSERT_X(ctor.parameterCount() == 1, "fromVerifiedType",
|
||||||
"Value type constructor must take exactly one argument");
|
"Value type constructor must take exactly one argument");
|
||||||
callConstructor(mo, ctorIndex, sData, allocate());
|
callConstructor(targetMetaObject, ctorIndex, source, allocate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename Allocate>
|
template<typename Allocate>
|
||||||
static bool fromMatchingType(
|
static bool fromMatchingType(
|
||||||
const QMetaObject *mo, const QV4::Value &s, Allocate &&allocate)
|
const QMetaObject *targetMetaObject, const QV4::Value &source, Allocate &&allocate)
|
||||||
{
|
{
|
||||||
for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
|
for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) {
|
||||||
const QMetaMethod ctor = mo->constructor(i);
|
const QMetaMethod ctor = targetMetaObject->constructor(i);
|
||||||
if (ctor.parameterCount() != 1)
|
if (ctor.parameterCount() != 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QMetaType parameterType = ctor.parameterMetaType(0);
|
const QMetaType parameterType = ctor.parameterMetaType(0);
|
||||||
QVariant parameter = QV4::ExecutionEngine::toVariant(s, parameterType);
|
QVariant parameter = QV4::ExecutionEngine::toVariant(source, parameterType);
|
||||||
if (parameter.metaType() == parameterType) {
|
if (parameter.metaType() == parameterType) {
|
||||||
callConstructor(mo, i, parameter.data(), allocate());
|
callConstructor(targetMetaObject, i, parameter.data(), allocate());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ static bool fromMatchingType(
|
||||||
QVariant converted(parameterType);
|
QVariant converted(parameterType);
|
||||||
if (QMetaType::convert(parameter.metaType(), parameter.constData(),
|
if (QMetaType::convert(parameter.metaType(), parameter.constData(),
|
||||||
parameterType, converted.data())) {
|
parameterType, converted.data())) {
|
||||||
callConstructor(mo, i, converted.data(), allocate());
|
callConstructor(targetMetaObject, i, converted.data(), allocate());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,23 +136,25 @@ static bool fromMatchingType(
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Allocate>
|
template<typename Allocate>
|
||||||
static bool fromMatchingType(const QMetaObject *mo, QVariant s, Allocate &&allocate)
|
static bool fromMatchingType(
|
||||||
|
const QMetaObject *targetMetaObject, QVariant source, Allocate &&allocate)
|
||||||
{
|
{
|
||||||
const QMetaType sourceMetaType = s.metaType();
|
const QMetaType sourceMetaType = source.metaType();
|
||||||
if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
|
if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
|
||||||
QJSValue val = s.value<QJSValue>();
|
QJSValue val = source.value<QJSValue>();
|
||||||
return fromMatchingType(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)),
|
return fromMatchingType(
|
||||||
std::forward<Allocate>(allocate));
|
targetMetaObject, QV4::Value(QJSValuePrivate::asReturnedValue(&val)),
|
||||||
|
std::forward<Allocate>(allocate));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
|
for (int i = 0, end = targetMetaObject->constructorCount(); i < end; ++i) {
|
||||||
const QMetaMethod ctor = mo->constructor(i);
|
const QMetaMethod ctor = targetMetaObject->constructor(i);
|
||||||
if (ctor.parameterCount() != 1)
|
if (ctor.parameterCount() != 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QMetaType parameterType = ctor.parameterMetaType(0);
|
const QMetaType parameterType = ctor.parameterMetaType(0);
|
||||||
if (sourceMetaType == parameterType) {
|
if (sourceMetaType == parameterType) {
|
||||||
callConstructor(mo, i, s.data(), allocate());
|
callConstructor(targetMetaObject, i, source.data(), allocate());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,8 +163,9 @@ static bool fromMatchingType(const QMetaObject *mo, QVariant s, Allocate &&alloc
|
||||||
// At this point, s should be a builtin type. For builtin types
|
// At this point, s should be a builtin type. For builtin types
|
||||||
// the QMetaType converters are good enough.
|
// the QMetaType converters are good enough.
|
||||||
QVariant parameter(parameterType);
|
QVariant parameter(parameterType);
|
||||||
if (QMetaType::convert(sourceMetaType, s.constData(), parameterType, parameter.data())) {
|
if (QMetaType::convert(
|
||||||
callConstructor(mo, i, parameter.data(), allocate());
|
sourceMetaType, source.constData(), parameterType, parameter.data())) {
|
||||||
|
callConstructor(targetMetaObject, i, parameter.data(), allocate());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,14 +190,42 @@ static bool fromString(const QMetaObject *mo, QString s, Allocate &&allocate)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void doWriteProperties(const QMetaObject *mo, const QV4::Value &s, void *target)
|
template<typename Get, typename Convert>
|
||||||
|
static bool doWriteProperty(const QMetaProperty &metaProperty, void *target,
|
||||||
|
Get &&get, Convert &&convert)
|
||||||
{
|
{
|
||||||
const QV4::Object *o = static_cast<const QV4::Object *>(&s);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Scope scope(o->engine());
|
||||||
QV4::ScopedObject object(scope, o);
|
QV4::ScopedObject object(scope, o);
|
||||||
|
|
||||||
for (int i = 0; i < mo->propertyCount(); ++i) {
|
for (int i = 0; i < targetMetaObject->propertyCount(); ++i) {
|
||||||
const QMetaProperty metaProperty = mo->property(i);
|
const QMetaProperty metaProperty = targetMetaObject->property(i);
|
||||||
const QString propertyName = QString::fromUtf8(metaProperty.name());
|
const QString propertyName = QString::fromUtf8(metaProperty.name());
|
||||||
|
|
||||||
QV4::ScopedString v4PropName(scope, scope.engine->newString(propertyName));
|
QV4::ScopedString v4PropName(scope, scope.engine->newString(propertyName));
|
||||||
|
@ -205,6 +236,14 @@ static void doWriteProperties(const QMetaObject *mo, const QV4::Value &s, void *
|
||||||
if (v4PropValue->isUndefined())
|
if (v4PropValue->isUndefined())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (doWriteProperty(metaProperty, target, [&](const QMetaType &propertyType) {
|
||||||
|
return QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
|
||||||
|
}, [&](const QMetaType &propertyType) {
|
||||||
|
return QQmlValueTypeProvider::createValueType(v4PropValue, propertyType);
|
||||||
|
})) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const QMetaType propertyType = metaProperty.metaType();
|
const QMetaType propertyType = metaProperty.metaType();
|
||||||
QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
|
QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
|
||||||
if (property.metaType() == propertyType) {
|
if (property.metaType() == propertyType) {
|
||||||
|
@ -232,23 +271,175 @@ static void doWriteProperties(const QMetaObject *mo, const QV4::Value &s, void *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QVariant byProperties(const QMetaObject *mo, QMetaType metaType, const QV4::Value &s)
|
static QVariant byProperties(
|
||||||
|
const QMetaObject *targetMetaObject, QMetaType metaType, const QV4::Value &source)
|
||||||
{
|
{
|
||||||
if (!s.isObject() || !mo)
|
if (!source.isObject() || !targetMetaObject)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
QVariant result(metaType);
|
QVariant result(metaType);
|
||||||
doWriteProperties(mo, s, result.data());
|
doWriteProperties(targetMetaObject, result.data(), source);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QVariant byProperties(const QMetaObject *mo, QMetaType metaType, const QVariant &s)
|
template<typename Read>
|
||||||
|
static void doWriteProperties(
|
||||||
|
const QMetaObject *targetMetaObject, void *target,
|
||||||
|
const QMetaObject *sourceMetaObject, Read &&read)
|
||||||
{
|
{
|
||||||
if (!mo || s.metaType() != QMetaType::fromType<QJSValue>())
|
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 = QQmlValueTypeProvider::createValueType(property, propertyType);
|
||||||
|
if (converted.isValid()) {
|
||||||
|
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 QVariant byProperties(
|
||||||
|
const QMetaObject *targetMetaObject, QMetaType targetMetaType, QObject *source)
|
||||||
|
{
|
||||||
|
if (!source || !targetMetaObject)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
QJSValue val = s.value<QJSValue>();
|
QVariant result(targetMetaType);
|
||||||
return byProperties(mo, metaType, QV4::Value(QJSValuePrivate::asReturnedValue(&val)));
|
doWriteProperties(targetMetaObject, result.data(), source);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariant byProperties(
|
||||||
|
const QMetaObject *targetMetaObject, QMetaType targetMetaType,
|
||||||
|
const QMetaObject *sourceMetaObject, const void *source)
|
||||||
|
{
|
||||||
|
if (!source || !sourceMetaObject || !targetMetaObject)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
QVariant result(targetMetaType);
|
||||||
|
doWriteProperties(
|
||||||
|
targetMetaObject, result.data(), sourceMetaObject,
|
||||||
|
[source](const QMetaObject *sourceMetaObject, int sourceProperty) {
|
||||||
|
return sourceMetaObject->property(sourceProperty).readOnGadget(source);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = QQmlValueTypeProvider::createValueType(property, propertyType);
|
||||||
|
if (converted.isValid()) {
|
||||||
|
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>
|
||||||
|
QVariant byProperties(
|
||||||
|
const QMetaObject *targetMetaObject, QMetaType targetMetaType, const Map &source)
|
||||||
|
{
|
||||||
|
QVariant result(targetMetaType);
|
||||||
|
doWriteProperties(targetMetaObject, result.data(), source);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariant byProperties(
|
||||||
|
const QMetaObject *targetMetaObject, QMetaType targetMetaType, const QVariant &source)
|
||||||
|
{
|
||||||
|
if (!targetMetaObject)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (source.metaType() == QMetaType::fromType<QJSValue>()) {
|
||||||
|
QJSValue val = source.value<QJSValue>();
|
||||||
|
return byProperties(
|
||||||
|
targetMetaObject, targetMetaType, QV4::Value(QJSValuePrivate::asReturnedValue(&val)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.metaType() == QMetaType::fromType<QVariantMap>()) {
|
||||||
|
return byProperties(
|
||||||
|
targetMetaObject, targetMetaType,
|
||||||
|
*static_cast<const QVariantMap *>(source.constData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.metaType() == QMetaType::fromType<QVariantHash>()) {
|
||||||
|
return byProperties(
|
||||||
|
targetMetaObject, targetMetaType,
|
||||||
|
*static_cast<const QVariantHash *>(source.constData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.metaType().flags() & QMetaType::PointerToQObject)
|
||||||
|
return byProperties(targetMetaObject, targetMetaType, source.value<QObject *>());
|
||||||
|
|
||||||
|
if (const QMetaObject *sourceMeta = QQmlMetaType::metaObjectForValueType(source.metaType()))
|
||||||
|
return byProperties(targetMetaObject, targetMetaType, sourceMeta, source.constData());
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -258,37 +449,38 @@ static QVariant byProperties(const QMetaObject *mo, QMetaType metaType, const QV
|
||||||
* pre-constructed object. It also avoids the creation of a QVariant in most cases. It is less
|
* pre-constructed object. It also avoids the creation of a QVariant in most cases. It is less
|
||||||
* efficient if you're going to create a QVariant anyway.
|
* efficient if you're going to create a QVariant anyway.
|
||||||
*/
|
*/
|
||||||
bool QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType metaType, void *target)
|
bool QQmlValueTypeProvider::createValueType(
|
||||||
|
QMetaType targetMetaType, void *target, const QV4::Value &source)
|
||||||
{
|
{
|
||||||
if (!isConstructibleMetaType(metaType))
|
if (!isConstructibleMetaType(targetMetaType))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto destruct = [metaType, target]() {
|
auto destruct = [targetMetaType, target]() {
|
||||||
metaType.destruct(target);
|
targetMetaType.destruct(target);
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|
||||||
const QQmlType type = QQmlMetaType::qmlType(metaType);
|
const QQmlType type = QQmlMetaType::qmlType(targetMetaType);
|
||||||
if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
|
if (const QMetaObject *targetMeta = QQmlMetaType::metaObjectForValueType(type)) {
|
||||||
const auto warn = [&]() {
|
const auto warn = [&]() {
|
||||||
qWarning().noquote()
|
qWarning().noquote()
|
||||||
<< "Could not find any constructor for value type"
|
<< "Could not find any constructor for value type"
|
||||||
<< mo->className() << "to call with value" << s.toQStringNoThrow();
|
<< targetMeta->className() << "to call with value" << source.toQStringNoThrow();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type.canPopulateValueType()) {
|
if (type.canPopulateValueType()) {
|
||||||
if (s.isObject() && mo) {
|
if (source.isObject() && targetMeta) {
|
||||||
doWriteProperties(mo, s, target);
|
doWriteProperties(targetMeta, target, source);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (type.canConstructValueType()) {
|
if (type.canConstructValueType()) {
|
||||||
if (fromMatchingType(mo, s, destruct))
|
if (fromMatchingType(targetMeta, source, destruct))
|
||||||
return true;
|
return true;
|
||||||
warn();
|
warn();
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if (type.canConstructValueType()) {
|
} else if (type.canConstructValueType()) {
|
||||||
if (fromMatchingType(mo, s, destruct))
|
if (fromMatchingType(targetMeta, source, destruct))
|
||||||
return true;
|
return true;
|
||||||
warn();
|
warn();
|
||||||
}
|
}
|
||||||
|
@ -296,9 +488,9 @@ bool QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType metaT
|
||||||
|
|
||||||
if (const auto valueTypeFunction = type.createValueTypeFunction()) {
|
if (const auto valueTypeFunction = type.createValueTypeFunction()) {
|
||||||
const QVariant result
|
const QVariant result
|
||||||
= valueTypeFunction(QJSValuePrivate::fromReturnedValue(s.asReturnedValue()));
|
= valueTypeFunction(QJSValuePrivate::fromReturnedValue(source.asReturnedValue()));
|
||||||
if (result.metaType() == metaType) {
|
if (result.metaType() == targetMetaType) {
|
||||||
metaType.construct(destruct(), result.constData());
|
targetMetaType.construct(destruct(), result.constData());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,9 +204,9 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent)
|
||||||
class QQmlValueTypeProvider
|
class QQmlValueTypeProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool createValueType(const QV4::Value &, QMetaType, void *);
|
static bool createValueType(QMetaType targetMetaType, void *target, const QV4::Value &source);
|
||||||
static QVariant constructValueType(
|
static QVariant constructValueType(
|
||||||
QMetaType resultMetaType, const QMetaObject *resultMetaObject,
|
QMetaType targetMetaType, const QMetaObject *targetMetaObject,
|
||||||
int ctorIndex, void *ctorArg);
|
int ctorIndex, void *ctorArg);
|
||||||
|
|
||||||
static QVariant createValueType(const QJSValue &, QMetaType);
|
static QVariant createValueType(const QJSValue &, QMetaType);
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import QtQml
|
||||||
|
|
||||||
|
DelegateModel {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// useful object as model, int as modelData
|
||||||
|
property ListModel singularModel: ListModel {
|
||||||
|
ListElement {
|
||||||
|
x: 11
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
x: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// same, useful, object as model and modelData
|
||||||
|
property ListModel listModel: ListModel {
|
||||||
|
ListElement {
|
||||||
|
x: 13
|
||||||
|
y: 14
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
x: 15
|
||||||
|
y: 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 listModel
|
||||||
|
case 2: return array
|
||||||
|
case 3: 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
|
||||||
|
property point modelAnonymous: model[""]
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ private slots:
|
||||||
void redrawUponColumnChange();
|
void redrawUponColumnChange();
|
||||||
void nestedDelegates();
|
void nestedDelegates();
|
||||||
void universalModelData();
|
void universalModelData();
|
||||||
|
void typedModelData();
|
||||||
void deleteRace();
|
void deleteRace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -359,6 +360,92 @@ void tst_QQmlDelegateModel::universalModelData()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 < 4; ++i) {
|
||||||
|
if (i == 0) {
|
||||||
|
for (int j = 0; j < 3; ++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() + ":62:9: Unable to assign double to QPointF"));
|
||||||
|
QTest::ignoreMessage(
|
||||||
|
QtWarningMsg,
|
||||||
|
qPrintable(url.toString() + ":61:9: Unable to assign double to QPointF"));
|
||||||
|
}
|
||||||
|
|
||||||
|
delegateModel->setProperty("n", i);
|
||||||
|
QObject *delegate = delegateModel->object(0);
|
||||||
|
QVERIFY(delegate);
|
||||||
|
const QPointF modelItem = delegate->property("modelSelf").value<QPointF>();
|
||||||
|
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());
|
||||||
|
QCOMPARE(delegate->property("modelAnonymous"), QPointF());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
// list model with 2 roles
|
||||||
|
QCOMPARE(delegate->property("modelX"), 13.0);
|
||||||
|
QCOMPARE(delegate->property("modelDataX"), 13.0);
|
||||||
|
QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelItem));
|
||||||
|
QCOMPARE(delegate->property("modelDataSelf"), QVariant::fromValue(modelItem));
|
||||||
|
QCOMPARE(delegate->property("modelModelData"), QVariant::fromValue(modelItem));
|
||||||
|
QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelItem));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
// JS array of objects
|
||||||
|
QCOMPARE(delegate->property("modelX"), 17.0);
|
||||||
|
QCOMPARE(delegate->property("modelDataX"), 17.0);
|
||||||
|
|
||||||
|
const QPointF modelData = delegate->property("modelDataSelf").value<QPointF>();
|
||||||
|
QCOMPARE(modelData, QPointF(17, 18));
|
||||||
|
QCOMPARE(delegate->property("modelSelf"), QVariant::fromValue(modelData));
|
||||||
|
QCOMPARE(delegate->property("modelModelData").value<QPointF>(), modelData);
|
||||||
|
QCOMPARE(delegate->property("modelAnonymous").value<QPointF>(), modelData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
// 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));
|
||||||
|
QCOMPARE(delegate->property("modelAnonymous"), QVariant::fromValue(modelData));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
QFAIL("wrong model number");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void tst_QQmlDelegateModel::deleteRace()
|
void tst_QQmlDelegateModel::deleteRace()
|
||||||
{
|
{
|
||||||
QQmlEngine engine;
|
QQmlEngine engine;
|
||||||
|
|
Loading…
Reference in New Issue