Allow more options for creating value types from JS objects

We allow value types to be created
1. by calling Q_INVOKABLE constructors
2. by setting their values from properties of a JS object

Both have to be opted into by setting a class info. If opted into, these
options override the existing methods. When a a type can be created by
setting its properties, that implies you can also initialize it using an
invokable constructor. However, when given a JS object, the properties
method is used.

We keep this internal and undocumented for now. As the last try (the
create(QJSValue) methods and QJSValue ctors) was not that stellar, let's
first wait a bit and see if we're getting it right this time around.

Fixes: QTBUG-106480
Change-Id: I767230924afcba032d501846cc3263dad57b7bf0
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Ulf Hermann 2022-09-08 14:41:42 +02:00 committed by Volker Hilsheimer
parent b101be9be6
commit 3195b44e1c
27 changed files with 941 additions and 120 deletions

View File

@ -1302,3 +1302,98 @@
\sa QML_ELEMENT, QML_NAMED_ELEMENT, QML_SINGLETON, qmlRegisterType(), qmlRegisterSingletonType() \sa QML_ELEMENT, QML_NAMED_ELEMENT, QML_SINGLETON, qmlRegisterType(), qmlRegisterSingletonType()
*/ */
/*!
\macro QML_VALUE_TYPE(name)
\relates QQmlEngine
Declares the enclosing type or namespace to be available in QML, using \a name
as the name. The type has to be a value type and the name has to be lower case.
\code
class MyValueType
{
Q_GADGET
QML_VALUE_TYPE(myValueType)
// ...
};
\endcode
\sa {Choosing the Correct Integration Method Between C++ and QML}, QML_NAMED_ELEMENT
*/
/*!
\macro QML_CONSTRUCTIBLE_VALUE
\internal
\relates QQmlEngine
Marks the surrounding value type as constructible. That is, any \l Q_INVOKABLE
constructors of the type that take exactly one argument can be used when
assigning a JavaScript value to a property of this type.
You can declare a constructible value type as follows:
\code
class MyValueType
{
Q_GADGET
QML_VALUE_TYPE(myValueType)
QML_CONSTRUCTIBLE_VALUE
Q_INVOKABLE MyValueType(double d);
// ...
};
\endcode
With the above type, the following QML code will produce a \c MyValueType
value using the given constructor and assign it to the property.
\qml
QtObject {
property myValueType v: 5.4
}
\endqml
\sa QML_VALUE_TYPE
*/
/*!
\macro QML_STRUCTURED_VALUE
\internal
\relates QQmlEngine
Marks the surrounding value type as structured. Structured value types can
and will preferably be constructed property-by-property from a JavaScript
object. A structured value type, however is always \l QML_CONSTRUCTIBLE_VALUE,
too. This means, you can still provide \l Q_INVOKABLE constructors in order to
handle construction from primitive types.
You can declare a structured value type as follows:
\code
class MyValueType
{
Q_GADGET
QML_VALUE_TYPE(myValueType)
QML_STRUCTURED_VALUE
Q_PROPERTY(double d READ d WRITE setD)
Q_PROPERTY(string e READ e WRITE setE)
// ...
};
\endcode
Then you can populate a property of this type as follows:
\qml
QtObject {
property myValueType v: ({d: 4.4, e: "a string"})
}
\endqml
The extra parentheses are necessary to disambiguate the JavaScript object
from what might be interpreted as a JavaScript code block.
\sa QML_VALUE_TYPE QML_CONSTRUCTIBLE_VALUE
*/

View File

@ -1580,7 +1580,13 @@ static QVariant toVariant(
QV4::ScopedValue arrayValue(scope); QV4::ScopedValue arrayValue(scope);
for (qint64 i = 0; i < length; ++i) { for (qint64 i = 0; i < length; ++i) {
arrayValue = a->get(i); arrayValue = a->get(i);
QVariant asVariant; QVariant asVariant(valueMetaType);
if (QQmlValueTypeProvider::createValueType(
arrayValue, valueMetaType, asVariant.data())) {
retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
continue;
}
if (QMetaType::canConvert(QMetaType::fromType<QJSValue>(), valueMetaType)) { if (QMetaType::canConvert(QMetaType::fromType<QJSValue>(), valueMetaType)) {
// before attempting a conversion from the concrete types, // before attempting a conversion from the concrete types,
// check if there exists a conversion from QJSValue -> out type // check if there exists a conversion from QJSValue -> out type
@ -1600,10 +1606,12 @@ static QVariant toVariant(
auto originalType = asVariant.metaType(); auto originalType = asVariant.metaType();
bool couldConvert = asVariant.convert(valueMetaType); bool couldConvert = asVariant.convert(valueMetaType);
if (!couldConvert) { if (!couldConvert) {
qWarning() << QLatin1String("Could not convert array value at position %1 from %2 to %3") qWarning().noquote()
.arg(QString::number(i), << QLatin1String("Could not convert array value "
QString::fromUtf8(originalType.name()), "at position %1 from %2 to %3")
QString::fromUtf8(valueMetaType.name())); .arg(QString::number(i),
QString::fromUtf8(originalType.name()),
QString::fromUtf8(valueMetaType.name()));
// create default constructed value // create default constructed value
asVariant = QVariant(valueMetaType, nullptr); asVariant = QVariant(valueMetaType, nullptr);
} }
@ -1662,6 +1670,12 @@ static QVariant toVariant(
return re->toQRegularExpression(); return re->toQRegularExpression();
#endif #endif
if (metaType.isValid() && !(metaType.flags() & QMetaType::PointerToQObject)) {
QVariant result(metaType);
if (QQmlValueTypeProvider::createValueType(value, metaType, result.data()))
return result;
}
if (createJSValueForObjects) if (createJSValueForObjects)
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue())); return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
@ -2504,8 +2518,17 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
} }
break; break;
} }
#if QT_CONFIG(qml_locale)
case QMetaType::QLocale: {
if (const QV4::QQmlLocaleData *l = value.as<QQmlLocaleData>()) {
*reinterpret_cast<QLocale *>(data) = *l->d()->locale;
return true;
}
break;
}
#endif
default: default:
; break;
} }
if (metaType.flags() & QMetaType::IsEnumeration) { if (metaType.flags() & QMetaType::IsEnumeration) {
@ -2579,14 +2602,8 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue()); QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue());
return true; return true;
} else if (!isPointer) { } else if (!isPointer) {
QVariant val; if (QQmlValueTypeProvider::createValueType(value, metaType, data))
if (QQmlValueTypeProvider::createValueType(
metaType, QJSValuePrivate::fromReturnedValue(value.asReturnedValue()), val)) {
Q_ASSERT(val.metaType() == metaType);
metaType.destruct(data);
metaType.construct(data, val.constData());
return true; return true;
}
} }
if (const QV4::Sequence *sequence = value.as<Sequence>()) { if (const QV4::Sequence *sequence = value.as<Sequence>()) {

View File

@ -648,12 +648,20 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
meta->addValueAtEnd(result.data(), &variant); meta->addValueAtEnd(result.data(), &variant);
} else { } else {
const QMetaType originalType = variant.metaType(); const QMetaType originalType = variant.metaType();
if (originalType != valueMetaType && !variant.convert(valueMetaType)) { if (originalType != valueMetaType) {
qWarning() << QLatin1String( QVariant converted(valueMetaType);
"Could not convert array value at position %1 from %2 to %3") if (QQmlValueTypeProvider::createValueType(
.arg(QString::number(i), QString::fromUtf8(originalType.name()), variant, valueMetaType, converted.data())) {
QString::fromUtf8(valueMetaType.name())); variant = converted;
variant = QVariant(valueMetaType); } else if (!variant.convert(valueMetaType)) {
qWarning().noquote()
<< 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 = converted;
}
} }
meta->addValueAtEnd(result.data(), variant.constData()); meta->addValueAtEnd(result.data(), variant.constData());
} }

View File

@ -166,7 +166,8 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
nullptr, nullptr,
QTypeRevision::zero(), QTypeRevision::zero(),
-1 -1,
QQmlPrivate::ValueTypeCreationMethod::None
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -464,11 +465,19 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
&& boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true); && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
QString noCreateReason; QString noCreateReason;
ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
if (!creatable) { if (!creatable) {
noCreateReason = QString::fromUtf8(classInfo(type.classInfoMetaObject, "QML.UncreatableReason")); noCreateReason = QString::fromUtf8(
classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
if (noCreateReason.isEmpty()) if (noCreateReason.isEmpty())
noCreateReason = QLatin1String("Type cannot be created in QML."); noCreateReason = QLatin1String("Type cannot be created in QML.");
} else if (!(type.typeId.flags() & QMetaType::PointerToQObject)) {
const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
if (qstrcmp(method, "structured") == 0)
creationMethod = ValueTypeCreationMethod::Structured;
else if (qstrcmp(method, "construct") == 0)
creationMethod = ValueTypeCreationMethod::Construct;
} }
RegisterType typeRevision = { RegisterType typeRevision = {
@ -493,7 +502,8 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
type.extensionMetaObject, type.extensionMetaObject,
nullptr, nullptr,
QTypeRevision(), QTypeRevision(),
type.structVersion > 0 ? type.finalizerCast : -1 type.structVersion > 0 ? type.finalizerCast : -1,
creationMethod
}; };
QQmlPrivate::RegisterSequentialContainer sequenceRevision = { QQmlPrivate::RegisterSequentialContainer sequenceRevision = {

View File

@ -78,7 +78,8 @@ int qmlRegisterAnonymousType(const char *uri, int versionMajor)
nullptr, nullptr,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -115,7 +116,8 @@ int qmlRegisterAnonymousType(const char *uri, int versionMajor)
nullptr, nullptr,
QTypeRevision::fromMinorVersion(metaObjectRevisionMinor), QTypeRevision::fromMinorVersion(metaObjectRevisionMinor),
QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -160,7 +162,8 @@ int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMin
nullptr, nullptr,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -193,7 +196,8 @@ int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMin
nullptr, nullptr,
QTypeRevision::fromMinorVersion(metaObjectRevision), QTypeRevision::fromMinorVersion(metaObjectRevision),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -233,7 +237,8 @@ int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int ve
nullptr, nullptr,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -273,7 +278,8 @@ int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int ve
nullptr, nullptr,
QTypeRevision::fromMinorVersion(metaObjectRevision), QTypeRevision::fromMinorVersion(metaObjectRevision),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -306,7 +312,8 @@ int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const c
nullptr, nullptr,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -337,7 +344,8 @@ int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const c
nullptr, nullptr,
QTypeRevision::fromMinorVersion(metaObjectRevision), QTypeRevision::fromMinorVersion(metaObjectRevision),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -368,7 +376,8 @@ int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)
nullptr, nullptr,
QTypeRevision::fromMinorVersion(metaObjectRevision), QTypeRevision::fromMinorVersion(metaObjectRevision),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -401,7 +410,8 @@ int qmlRegisterExtendedType(const char *uri, int versionMajor)
nullptr, nullptr,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -440,7 +450,8 @@ int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor,
nullptr, nullptr,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -490,7 +501,8 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
parser, parser,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -522,7 +534,8 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
parser, parser,
QTypeRevision::fromMinorVersion(metaObjectRevision), QTypeRevision::fromMinorVersion(metaObjectRevision),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
@ -561,7 +574,8 @@ int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int version
parser, parser,
QTypeRevision::zero(), QTypeRevision::zero(),
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast() QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
QQmlPrivate::ValueTypeCreationMethod::None,
}; };
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);

View File

@ -457,11 +457,9 @@ QVariant QtObject::font(const QJSValue &fontSpecifier) const
} }
{ {
QVariant v; QVariant v((QMetaType(QMetaType::QFont)));
if (QQmlValueTypeProvider::createValueType( if (QQmlValueTypeProvider::constructFromJSValue(fontSpecifier, v.metaType(), v.data()))
QMetaType(QMetaType::QFont), fontSpecifier, v)) {
return v; return v;
}
} }
v4Engine()->throwError(QStringLiteral("Qt.font(): Invalid argument: " v4Engine()->throwError(QStringLiteral("Qt.font(): Invalid argument: "
@ -489,14 +487,14 @@ void addParameters(QJSEngine *e, QJSValue &result, int i, T parameter, Others...
} }
template<typename ...T> template<typename ...T>
static QVariant createValueType(QJSEngine *e, QMetaType type, T... parameters) static QVariant constructFromJSValue(QJSEngine *e, QMetaType type, T... parameters)
{ {
if (!e) if (!e)
return QVariant(); return QVariant();
QJSValue params = e->newArray(sizeof...(parameters)); QJSValue params = e->newArray(sizeof...(parameters));
addParameters(e, params, 0, parameters...); addParameters(e, params, 0, parameters...);
QVariant variant; QVariant variant(type);
QQmlValueTypeProvider::createValueType(type, params, variant); QQmlValueTypeProvider::constructFromJSValue(params, type, variant.data());
return variant; return variant;
} }
@ -507,7 +505,7 @@ static QVariant createValueType(QJSEngine *e, QMetaType type, T... parameters)
*/ */
QVariant QtObject::vector2d(double x, double y) const QVariant QtObject::vector2d(double x, double y) const
{ {
return createValueType(jsEngine(), QMetaType(QMetaType::QVector2D), x, y); return constructFromJSValue(jsEngine(), QMetaType(QMetaType::QVector2D), x, y);
} }
/*! /*!
@ -517,7 +515,7 @@ QVariant QtObject::vector2d(double x, double y) const
*/ */
QVariant QtObject::vector3d(double x, double y, double z) const QVariant QtObject::vector3d(double x, double y, double z) const
{ {
return createValueType(jsEngine(), QMetaType(QMetaType::QVector3D), x, y, z); return constructFromJSValue(jsEngine(), QMetaType(QMetaType::QVector3D), x, y, z);
} }
/*! /*!
@ -527,7 +525,7 @@ QVariant QtObject::vector3d(double x, double y, double z) const
*/ */
QVariant QtObject::vector4d(double x, double y, double z, double w) const QVariant QtObject::vector4d(double x, double y, double z, double w) const
{ {
return createValueType(jsEngine(), QMetaType(QMetaType::QVector4D), x, y, z, w); return constructFromJSValue(jsEngine(), QMetaType(QMetaType::QVector4D), x, y, z, w);
} }
/*! /*!
@ -537,7 +535,7 @@ QVariant QtObject::vector4d(double x, double y, double z, double w) const
*/ */
QVariant QtObject::quaternion(double scalar, double x, double y, double z) const QVariant QtObject::quaternion(double scalar, double x, double y, double z) const
{ {
return createValueType(jsEngine(), QMetaType(QMetaType::QQuaternion), scalar, x, y, z); return constructFromJSValue(jsEngine(), QMetaType(QMetaType::QQuaternion), scalar, x, y, z);
} }
/*! /*!
@ -563,16 +561,17 @@ QVariant QtObject::quaternion(double scalar, double x, double y, double z) const
*/ */
QVariant QtObject::matrix4x4() const QVariant QtObject::matrix4x4() const
{ {
QVariant variant; QVariant variant((QMetaType(QMetaType::QMatrix4x4)));
QQmlValueTypeProvider::createValueType(QMetaType(QMetaType::QMatrix4x4), QJSValue(), variant); QQmlValueTypeProvider::constructFromJSValue(
QJSValue(), variant.metaType(), variant.data());
return variant; return variant;
} }
QVariant QtObject::matrix4x4(const QJSValue &value) const QVariant QtObject::matrix4x4(const QJSValue &value) const
{ {
if (value.isObject()) { if (value.isObject()) {
QVariant v; QVariant v((QMetaType(QMetaType::QMatrix4x4)));
if (QQmlValueTypeProvider::createValueType(QMetaType(QMetaType::QMatrix4x4), value, v)) if (QQmlValueTypeProvider::constructFromJSValue(value, v.metaType(), v.data()))
return v; return v;
} }
@ -586,7 +585,7 @@ QVariant QtObject::matrix4x4(double m11, double m12, double m13, double m14,
double m31, double m32, double m33, double m34, double m31, double m32, double m33, double m34,
double m41, double m42, double m43, double m44) const double m41, double m42, double m43, double m44) const
{ {
return createValueType(jsEngine(), QMetaType(QMetaType::QMatrix4x4), return constructFromJSValue(jsEngine(), QMetaType(QMetaType::QMatrix4x4),
m11, m12, m13, m14, m21, m22, m23, m24, m11, m12, m13, m14, m21, m22, m23, m24,
m31, m32, m33, m34, m41, m42, m43, m44); m31, m32, m33, m34, m41, m42, m43, m44);
} }

View File

@ -3,6 +3,7 @@
#include <private/qqmlglobal_p.h> #include <private/qqmlglobal_p.h>
#include <QtQml/private/qqmlmetatype_p.h> #include <QtQml/private/qqmlmetatype_p.h>
#include <QtQml/private/qjsvalue_p.h>
#include <QtQml/qqmlengine.h> #include <QtQml/qqmlengine.h>
#include <QtCore/qvariant.h> #include <QtCore/qvariant.h>
@ -12,13 +13,237 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
bool QQmlValueTypeProvider::createValueType(QMetaType metaType, const QJSValue &s, QVariant &data) // Pre-filter the metatype before poking QQmlMetaType::qmlType() and locking its mutex.
static bool isConstructibleMetaType(const QMetaType metaType)
{ {
const QQmlType qmlType = QQmlMetaType::qmlType(metaType); switch (metaType.id()) {
if (auto valueTypeFunction = qmlType.createValueTypeFunction()) { // The builtins are not constructible this way.
case QMetaType::Void:
case QMetaType::Nullptr:
case QMetaType::QVariant:
case QMetaType::Int:
case QMetaType::UInt:
case QMetaType::LongLong:
case QMetaType::ULongLong:
case QMetaType::Float:
case QMetaType::Double:
case QMetaType::Long:
case QMetaType::ULong:
case QMetaType::Short:
case QMetaType::UShort:
case QMetaType::Char:
case QMetaType::SChar:
case QMetaType::UChar:
case QMetaType::QChar:
case QMetaType::QString:
case QMetaType::Bool:
case QMetaType::QDateTime:
case QMetaType::QDate:
case QMetaType::QTime:
case QMetaType::QUrl:
case QMetaType::QRegularExpression:
case QMetaType::QByteArray:
case QMetaType::QLocale:
return false;
default:
break;
}
// QJSValue is also builtin
if (metaType == QMetaType::fromType<QJSValue>())
return false;
// We also don't want to construct pointers of any kind, or lists, or enums.
if (metaType.flags() &
(QMetaType::PointerToQObject
| QMetaType::IsEnumeration
| QMetaType::SharedPointerToQObject
| QMetaType::WeakPointerToQObject
| QMetaType::TrackingPointerToQObject
| QMetaType::IsUnsignedEnumeration
| QMetaType::PointerToGadget
| QMetaType::IsPointer
| QMetaType::IsQmlList)) {
return false;
}
return true;
}
static void callConstructor(
const QMetaObject *mo, int i, void *parameter, QMetaType metaType, void *data)
{
// Unfortunately CreateInstance unconditionally creates the instance on the heap.
void *gadget = nullptr;
void *p[] = { &gadget, parameter };
mo->static_metacall(QMetaObject::CreateInstance, i, p);
Q_ASSERT(gadget);
metaType.destruct(data);
metaType.construct(data, gadget);
metaType.destroy(gadget);
}
static bool fromMatchingType(
const QMetaObject *mo, const QV4::Value &s, const QMetaType metaType, void *data)
{
for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
const QMetaMethod ctor = mo->constructor(i);
if (ctor.parameterCount() != 1)
continue;
const QMetaType parameterType = ctor.parameterMetaType(0);
QVariant parameter = QV4::ExecutionEngine::toVariant(s, parameterType);
if (parameter.metaType() == parameterType) {
callConstructor(mo, i, parameter.data(), metaType, data);
return true;
}
QVariant converted(parameterType);
if (QQmlValueTypeProvider::createValueType(s, parameterType, converted.data())) {
callConstructor(mo, i, converted.data(), metaType, data);
return true;
}
if (QMetaType::convert(parameter.metaType(), parameter.constData(),
parameterType, converted.data())) {
callConstructor(mo, i, converted.data(), metaType, data);
return true;
}
}
return false;
}
static bool fromMatchingType(
const QMetaObject *mo, QVariant s, const QMetaType metaType, void *data)
{
const QMetaType sourceMetaType = s.metaType();
if (sourceMetaType == QMetaType::fromType<QJSValue>()) {
QJSValue val = s.value<QJSValue>();
return fromMatchingType(
mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), metaType, data);
}
for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
const QMetaMethod ctor = mo->constructor(i);
if (ctor.parameterCount() != 1)
continue;
const QMetaType parameterType = ctor.parameterMetaType(0);
if (sourceMetaType == parameterType) {
callConstructor(mo, i, s.data(), metaType, data);
return true;
}
QVariant parameter(parameterType);
if (QQmlValueTypeProvider::createValueType(s, parameterType, parameter.data())) {
callConstructor(mo, i, parameter.data(), metaType, data);
return true;
}
// At this point, s should be a builtin type. For builtin types
// the QMetaType converters are good enough.
if (QMetaType::convert(sourceMetaType, s.constData(), parameterType, parameter.data())) {
callConstructor(mo, i, s.data(), metaType, data);
return true;
}
}
return false;
}
static bool fromString(
const QMetaObject *mo, QString s, const QMetaType metaType, void *data)
{
for (int i = 0, end = mo->constructorCount(); i < end; ++i) {
const QMetaMethod ctor = mo->constructor(i);
if (ctor.parameterCount() != 1)
continue;
if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) {
callConstructor(mo, i, &s, metaType, data);
return true;
}
}
return false;
}
static bool byProperties(
const QMetaObject *mo, const QV4::Value &s, void *data)
{
if (!s.isObject())
return false;
if (!mo)
return false;
const QV4::Object *o = static_cast<const QV4::Object *>(&s);
QV4::Scope scope(o->engine());
QV4::ScopedObject object(scope, o);
for (int i = 0; i < mo->propertyCount(); ++i) {
const QMetaProperty metaProperty = mo->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;
const QMetaType propertyType = metaProperty.metaType();
QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType);
if (property.metaType() == propertyType) {
metaProperty.writeOnGadget(data, property);
continue;
}
QVariant converted(propertyType);
if (QQmlValueTypeProvider::createValueType(v4PropValue, propertyType, converted.data())) {
metaProperty.writeOnGadget(data, converted);
continue;
}
if (QMetaType::convert(property.metaType(), property.constData(),
propertyType, converted.data())) {
metaProperty.writeOnGadget(data, converted);
continue;
}
qWarning().noquote()
<< QLatin1String("Could not convert %1 to %2 for property %3")
.arg(v4PropValue->toQStringNoThrow(), QString::fromUtf8(propertyType.name()),
propertyName);
}
return true;
}
static bool byProperties(
const QMetaObject *mo, const QVariant &s, void *data)
{
if (!mo)
return false;
if (s.metaType() == QMetaType::fromType<QJSValue>()) {
QJSValue val = s.value<QJSValue>();
return byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), data);
}
return false;
}
static bool fromJSValue(
const QQmlType &type, const QJSValue &s, QMetaType metaType, void *data)
{
if (const auto valueTypeFunction = type.createValueTypeFunction()) {
QVariant result = valueTypeFunction(s); QVariant result = valueTypeFunction(s);
if (result.metaType() == metaType) { if (result.metaType() == metaType) {
data = std::move(result); metaType.destruct(data);
metaType.construct(data, result.constData());
return true; return true;
} }
} }
@ -26,6 +251,98 @@ bool QQmlValueTypeProvider::createValueType(QMetaType metaType, const QJSValue &
return false; return false;
} }
bool QQmlValueTypeProvider::constructFromJSValue(
const QJSValue &s, QMetaType metaType, void *data)
{
return isConstructibleMetaType(metaType)
&& fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType, data);
}
bool QQmlValueTypeProvider::createValueType(
const QString &s, QMetaType metaType, void *data)
{
if (!isConstructibleMetaType(metaType))
return false;
const QQmlType type = QQmlMetaType::qmlType(metaType);
const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type);
if (mo && type.canConstructValueType()) {
if (fromString(mo, s, metaType, data))
return true;
}
return fromJSValue(type, s, metaType, data);
}
bool QQmlValueTypeProvider::createValueType(
const QJSValue &s, QMetaType metaType, void *data)
{
if (!isConstructibleMetaType(metaType))
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)) {
return true;
}
if (type.canConstructValueType()
&& fromMatchingType(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)),
metaType, data)) {
return true;
}
}
return constructFromJSValue(s, metaType, data);
}
bool QQmlValueTypeProvider::createValueType(
const QV4::Value &s, QMetaType metaType, void *data)
{
if (!isConstructibleMetaType(metaType))
return false;
const QQmlType type = QQmlMetaType::qmlType(metaType);
if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
if (type.canPopulateValueType() && byProperties(mo, s, data))
return true;
if (type.canConstructValueType()) {
if (fromMatchingType(mo, s, metaType, data))
return true;
qWarning().noquote()
<< "Could not find any constructor for value type"
<< mo->className() << "to call with value" << s.toQStringNoThrow();
}
}
return constructFromJSValue(
QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType, data);
}
/*!
* \internal
* This should only be called with either builtin types or wrapped QJSValues as source.
*/
bool QQmlValueTypeProvider::createValueType(
const QVariant &s, QMetaType metaType, void *data)
{
if (!isConstructibleMetaType(metaType))
return false;
const QQmlType type = QQmlMetaType::qmlType(metaType);
if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
if (type.canPopulateValueType() && byProperties(mo, s, data))
return true;
if (type.canConstructValueType()) {
if (fromMatchingType(mo, s, metaType, data))
return true;
qWarning().noquote()
<< "Could not find any constructor for value type"
<< mo->className() << "to call with value" << s;
}
}
return false;
}
QQmlColorProvider::~QQmlColorProvider() {} QQmlColorProvider::~QQmlColorProvider() {}
QVariant QQmlColorProvider::colorFromString(const QString &, bool *ok) { if (ok) *ok = false; return QVariant(); } QVariant QQmlColorProvider::colorFromString(const QString &, bool *ok) { if (ok) *ok = false; return QVariant(); }
unsigned QQmlColorProvider::rgbaFromString(const QString &, bool *ok) { if (ok) *ok = false; return 0; } unsigned QQmlColorProvider::rgbaFromString(const QString &, bool *ok) { if (ok) *ok = false; return 0; }

View File

@ -185,7 +185,12 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent)
class QQmlValueTypeProvider class QQmlValueTypeProvider
{ {
public: public:
static bool createValueType(QMetaType, const QJSValue &, QVariant &); static bool constructFromJSValue(const QJSValue &, QMetaType, void *);
static bool createValueType(const QString &, QMetaType, void *);
static bool createValueType(const QJSValue &, QMetaType, void *);
static bool createValueType(const QV4::Value &, QMetaType, void *);
static bool createValueType(const QVariant &, QMetaType, void *);
}; };
class Q_QML_PRIVATE_EXPORT QQmlColorProvider class Q_QML_PRIVATE_EXPORT QQmlColorProvider

View File

@ -152,6 +152,10 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser); d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
d->extraData.cd->registerEnumClassesUnscoped = true; d->extraData.cd->registerEnumClassesUnscoped = true;
d->extraData.cd->registerEnumsFromRelatedTypes = true; d->extraData.cd->registerEnumsFromRelatedTypes = true;
d->extraData.cd->constructValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
&& type.creationMethod != QQmlPrivate::ValueTypeCreationMethod::None;
d->extraData.cd->populateValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
&& type.creationMethod == QQmlPrivate::ValueTypeCreationMethod::Structured;
if (type.extensionMetaObject) if (type.extensionMetaObject)
d->extraData.cd->extMetaObject = type.extensionMetaObject; d->extraData.cd->extMetaObject = type.extensionMetaObject;
@ -1707,23 +1711,8 @@ const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
// call QObject pointers value types. Explicitly registered types also override // call QObject pointers value types. Explicitly registered types also override
// the implicit use of gadgets. // the implicit use of gadgets.
if (!(metaType.flags() & QMetaType::PointerToQObject)) { if (!(metaType.flags() & QMetaType::PointerToQObject)) {
const QQmlType qmlType = QQmlMetaType::qmlType(metaType); if (const QMetaObject *mo = metaObjectForValueType(QQmlMetaType::qmlType(metaType)))
return mo;
// Prefer the extension meta object, if any.
// Extensions allow registration of non-gadget value types.
if (const QMetaObject *extensionMetaObject = qmlType.extensionMetaObject()) {
// This may be a namespace even if the original metaType isn't.
// You can do such things with QML_FOREIGN declarations.
if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
return extensionMetaObject;
}
if (const QMetaObject *qmlTypeMetaObject = qmlType.metaObject()) {
// This may be a namespace even if the original metaType isn't.
// You can do such things with QML_FOREIGN declarations.
if (qmlTypeMetaObject->metaType().flags() & QMetaType::IsGadget)
return qmlTypeMetaObject;
}
} }
// If it _is_ a gadget, we can just use it. // If it _is_ a gadget, we can just use it.

View File

@ -240,6 +240,26 @@ public:
static bool isValueType(QMetaType type); static bool isValueType(QMetaType type);
static QQmlValueType *valueType(QMetaType metaType); static QQmlValueType *valueType(QMetaType metaType);
static const QMetaObject *metaObjectForValueType(QMetaType type); static const QMetaObject *metaObjectForValueType(QMetaType type);
static const QMetaObject *metaObjectForValueType(const QQmlType &qmlType)
{
// Prefer the extension meta object, if any.
// Extensions allow registration of non-gadget value types.
if (const QMetaObject *extensionMetaObject = qmlType.extensionMetaObject()) {
// This may be a namespace even if the original metaType isn't.
// You can do such things with QML_FOREIGN declarations.
if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
return extensionMetaObject;
}
if (const QMetaObject *qmlTypeMetaObject = qmlType.metaObject()) {
// This may be a namespace even if the original metaType isn't.
// You can do such things with QML_FOREIGN declarations.
if (qmlTypeMetaObject->metaType().flags() & QMetaType::IsGadget)
return qmlTypeMetaObject;
}
return nullptr;
}
static QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t); static QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t);
static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);

View File

@ -425,9 +425,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
} }
break; break;
case QMetaType::QColor: { case QMetaType::QColor: {
QVariant data; QVariant data(propertyType);
if (QQmlValueTypeProvider::createValueType( if (QQmlValueTypeProvider::createValueType(
propertyType, compilationUnit->bindingValueAsString(binding), data)) { compilationUnit->bindingValueAsString(binding), propertyType, data.data())) {
property->writeProperty(_qobject, data.data(), propertyWriteFlags); property->writeProperty(_qobject, data.data(), propertyWriteFlags);
} }
} }
@ -508,9 +508,10 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
case QMetaType::QVector3D: case QMetaType::QVector3D:
case QMetaType::QVector4D: case QMetaType::QVector4D:
case QMetaType::QQuaternion: { case QMetaType::QQuaternion: {
QVariant result; QVariant result(propertyType);
bool ok = QQmlValueTypeProvider::createValueType( bool ok = QQmlValueTypeProvider::createValueType(
propertyType, compilationUnit->bindingValueAsString(binding), result); compilationUnit->bindingValueAsString(binding),
result.metaType(), result.data());
assertOrNull(ok); assertOrNull(ok);
Q_UNUSED(ok); Q_UNUSED(ok);
property->writeProperty(_qobject, result.data(), propertyWriteFlags); property->writeProperty(_qobject, result.data(), propertyWriteFlags);
@ -576,6 +577,35 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
} }
property->writeProperty(_qobject, &value, propertyWriteFlags); property->writeProperty(_qobject, &value, propertyWriteFlags);
break; break;
} else {
QVariant source;
switch (binding->type()) {
case QV4::CompiledData::Binding::Type_Boolean:
source = binding->valueAsBoolean();
break;
case QV4::CompiledData::Binding::Type_Number: {
const double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) == n)
source = int(n);
else
source = n;
break;
}
case QV4::CompiledData::Binding::Type_Null:
source = QVariant::fromValue<std::nullptr_t>(nullptr);
break;
case QV4::CompiledData::Binding::Type_Invalid:
break;
default:
source = compilationUnit->bindingValueAsString(binding);
break;
}
QVariant target(propertyType);
if (QQmlValueTypeProvider::createValueType(source, propertyType, target.data())) {
property->writeProperty(_qobject, target.data(), propertyWriteFlags);
break;
}
} }
// string converters are not exposed, so ending up here indicates an error // string converters are not exposed, so ending up here indicates an error

View File

@ -427,11 +427,14 @@ namespace QQmlPrivate
enum AutoParentResult { Parented, IncompatibleObject, IncompatibleParent }; enum AutoParentResult { Parented, IncompatibleObject, IncompatibleParent };
typedef AutoParentResult (*AutoParentFunction)(QObject *object, QObject *parent); typedef AutoParentResult (*AutoParentFunction)(QObject *object, QObject *parent);
enum class ValueTypeCreationMethod { None, Construct, Structured };
struct RegisterType { struct RegisterType {
enum StructVersion: int { enum StructVersion: int {
Base = 0, Base = 0,
FinalizerCast = 1, FinalizerCast = 1,
CurrentVersion = FinalizerCast, CreationMethod = 2,
CurrentVersion = CreationMethod,
}; };
bool has(StructVersion v) const { return structVersion >= int(v); } bool has(StructVersion v) const { return structVersion >= int(v); }
@ -446,6 +449,7 @@ namespace QQmlPrivate
void *userdata; void *userdata;
QString noCreationReason; QString noCreationReason;
// ### Qt7: Get rid of this. It can be covered by creationMethod below.
QVariant (*createValueType)(const QJSValue &); QVariant (*createValueType)(const QJSValue &);
const char *uri; const char *uri;
@ -467,6 +471,8 @@ namespace QQmlPrivate
QTypeRevision revision; QTypeRevision revision;
int finalizerCast; int finalizerCast;
ValueTypeCreationMethod creationMethod;
// If this is extended ensure "version" is bumped!!! // If this is extended ensure "version" is bumped!!!
}; };

View File

@ -1220,7 +1220,7 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx,
return false; return false;
QVariant v = value; QVariant v = value;
if (prop.isEnumType()) { if (prop.isEnumType() && v.metaType() != prop.metaType()) {
QMetaEnum menum = prop.enumerator(); QMetaEnum menum = prop.enumerator();
if (v.userType() == QMetaType::QString) { if (v.userType() == QMetaType::QString) {
bool ok; bool ok;
@ -1360,6 +1360,80 @@ private:
QUntypedPropertyBinding untypedBinding; QUntypedPropertyBinding untypedBinding;
}; };
struct ConvertAndAssignResult {
bool couldConvert = false;
bool couldWrite = false;
operator bool() const { return couldConvert; }
};
static ConvertAndAssignResult tryConvertAndAssign(
QObject *object, const QQmlPropertyData &property, const QVariant &value,
QQmlPropertyData::WriteFlags flags, QMetaType propertyMetaType, QMetaType variantMetaType,
bool isUrl) {
if (isUrl
|| variantMetaType == QMetaType::fromType<QString>()
|| propertyMetaType == QMetaType::fromType<QList<QUrl>>()
|| property.isQList()) {
return {false, false};
}
// common cases:
switch (propertyMetaType.id()) {
case QMetaType::Bool:
if (value.canConvert(propertyMetaType)) {
bool b = value.toBool();
return {true, property.writeProperty(object, &b, flags)};
}
return {false, false};
case QMetaType::Int: {
bool ok = false;
int i = value.toInt(&ok);
return {ok, ok && property.writeProperty(object, &i, flags)};
}
case QMetaType::UInt: {
bool ok = false;
uint u = value.toUInt(&ok);
return {ok, ok && property.writeProperty(object, &u, flags)};
}
case QMetaType::Double: {
bool ok = false;
double d = value.toDouble(&ok);
return {ok, ok && property.writeProperty(object, &d, flags)};
}
case QMetaType::Float: {
bool ok = false;
float f = value.toFloat(&ok);
return {ok, ok && property.writeProperty(object, &f, flags)};
}
case QMetaType::QString:
if (value.canConvert(propertyMetaType)) {
QString s = value.toString();
return {true, property.writeProperty(object, &s, flags)};
}
return {false, false};
case QMetaType::QVariantMap:
if (value.canConvert(propertyMetaType)) {
QVariantMap m = value.toMap();
return {true, property.writeProperty(object, &m, flags)};
}
return {false, false};
default: {
break;
}
}
QVariant converted(propertyMetaType);
if (QQmlValueTypeProvider::createValueType(value, propertyMetaType, converted.data())
|| QMetaType::convert(value.metaType(), value.constData(),
propertyMetaType, converted.data())) {
return {true, property.writeProperty(object, converted.data(), flags)};
}
return {false, false};
};
bool QQmlPropertyPrivate::write( bool QQmlPropertyPrivate::write(
QObject *object, const QQmlPropertyData &property, const QVariant &value, QObject *object, const QQmlPropertyData &property, const QVariant &value,
const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags) const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
@ -1419,37 +1493,9 @@ bool QQmlPropertyPrivate::write(
} else { } else {
return false; return false;
} }
} else if (value.canConvert(propertyMetaType) } else if (ConvertAndAssignResult result = tryConvertAndAssign(
&& !isUrl && variantMetaType != QMetaType::fromType<QString>() object, property, value, flags, propertyMetaType, variantMetaType, isUrl)) {
&& propertyMetaType != QMetaType::fromType<QList<QUrl>>() && !property.isQList()) { return result.couldWrite;
// common cases:
switch (propertyMetaType.id()) {
case QMetaType::Bool: {
bool b = value.toBool();
return property.writeProperty(object, &b, flags);
}
case QMetaType::Int: {
int i = value.toInt();
return property.writeProperty(object, &i, flags);
}
case QMetaType::Double: {
double d = value.toDouble();
return property.writeProperty(object, &d, flags);
}
case QMetaType::Float: {
float f = value.toFloat();
return property.writeProperty(object, &f, flags);
}
case QMetaType::QString: {
QString s = value.toString();
return property.writeProperty(object, &s, flags);
}
default: { // "fallback":
QVariant v = value;
v.convert(propertyMetaType);
return property.writeProperty(object, const_cast<void *>(v.constData()), flags);
}
}
} else if (propertyMetaType == QMetaType::fromType<QVariant>()) { } else if (propertyMetaType == QMetaType::fromType<QVariant>()) {
return property.writeProperty(object, const_cast<QVariant *>(&value), flags); return property.writeProperty(object, const_cast<QVariant *>(&value), flags);
} else if (isUrl) { } else if (isUrl) {

View File

@ -568,10 +568,10 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
default: return QString(); default: return QString();
} }
}; };
QVariant result; QVariant result(property->propType());
if (!QQmlValueTypeProvider::createValueType( if (!QQmlValueTypeProvider::createValueType(
property->propType(), compilationUnit->bindingValueAsString(binding),
compilationUnit->bindingValueAsString(binding), result)) { result.metaType(), result.data())) {
return warnOrError(tr("Invalid property assignment: %1 expected") return warnOrError(tr("Invalid property assignment: %1 expected")
.arg(typeName())); .arg(typeName()));
} }
@ -618,6 +618,8 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
} else if (property->isQObject() } else if (property->isQObject()
&& bindingType == QV4::CompiledData::Binding::Type_Null) { && bindingType == QV4::CompiledData::Binding::Type_Null) {
break; break;
} else if (QQmlMetaType::qmlType(property->propType()).canConstructValueType()) {
break;
} }
return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(property->propType().name()))); return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(property->propType().name())));

View File

@ -40,11 +40,15 @@ QVariant QQmlStringConverters::variantFromString(const QString &s, QMetaType pre
case QMetaType::QRect: case QMetaType::QRect:
return QVariant::fromValue(rectFFromString(s, ok).toRect()); return QVariant::fromValue(rectFFromString(s, ok).toRect());
default: { default: {
QVariant ret; QVariant ret(preferredType);
bool success = QQmlValueTypeProvider::createValueType(preferredType, QJSValue(s), ret); if (QQmlValueTypeProvider::createValueType(s, preferredType, ret.data())) {
if (ok)
*ok = true;
return ret;
}
if (ok) if (ok)
*ok = success; *ok = false;
return ret; return QVariant();
} }
} }
} }

View File

@ -527,6 +527,20 @@ QQmlType::CreateValueTypeFunc QQmlType::createValueTypeFunction() const
return d->extraData.cd->createValueTypeFunc; return d->extraData.cd->createValueTypeFunc;
} }
bool QQmlType::canConstructValueType() const
{
if (!d || d->regType != CppType)
return false;
return d->extraData.cd->constructValueType;
}
bool QQmlType::canPopulateValueType() const
{
if (!d || d->regType != CppType)
return false;
return d->extraData.cd->populateValueType;
}
QQmlType::CreateFunc QQmlType::createFunction() const QQmlType::CreateFunc QQmlType::createFunction() const
{ {
if (!d || d->regType != CppType) if (!d || d->regType != CppType)

View File

@ -71,6 +71,9 @@ public:
typedef QVariant (*CreateValueTypeFunc)(const QJSValue &); typedef QVariant (*CreateValueTypeFunc)(const QJSValue &);
CreateValueTypeFunc createValueTypeFunction() const; CreateValueTypeFunc createValueTypeFunction() const;
bool canConstructValueType() const;
bool canPopulateValueType() const;
QObject *create() const; QObject *create() const;
QObject *create(void **, size_t) const; QObject *create(void **, size_t) const;
QObject *createWithQQmlData() const; QObject *createWithQQmlData() const;

View File

@ -92,6 +92,8 @@ public:
int finalizerCast; int finalizerCast;
bool registerEnumClassesUnscoped; bool registerEnumClassesUnscoped;
bool registerEnumsFromRelatedTypes; bool registerEnumsFromRelatedTypes;
bool constructValueType;
bool populateValueType;
}; };
struct QQmlSingletonTypeData struct QQmlSingletonTypeData

View File

@ -100,6 +100,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlPointFValueType
QML_FOREIGN(QPointF) QML_FOREIGN(QPointF)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlPointFValueType) QML_EXTENDED(QQmlPointFValueType)
QML_STRUCTURED_VALUE
public: public:
Q_INVOKABLE QString toString() const; Q_INVOKABLE QString toString() const;
@ -119,6 +120,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlPointValueType
QML_FOREIGN(QPoint) QML_FOREIGN(QPoint)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlPointValueType) QML_EXTENDED(QQmlPointValueType)
QML_STRUCTURED_VALUE
public: public:
Q_INVOKABLE QString toString() const; Q_INVOKABLE QString toString() const;
@ -138,6 +140,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlSizeFValueType
QML_FOREIGN(QSizeF) QML_FOREIGN(QSizeF)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlSizeFValueType) QML_EXTENDED(QQmlSizeFValueType)
QML_STRUCTURED_VALUE
public: public:
Q_INVOKABLE QString toString() const; Q_INVOKABLE QString toString() const;
@ -157,6 +160,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlSizeValueType
QML_FOREIGN(QSize) QML_FOREIGN(QSize)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlSizeValueType) QML_EXTENDED(QQmlSizeValueType)
QML_STRUCTURED_VALUE
public: public:
Q_INVOKABLE QString toString() const; Q_INVOKABLE QString toString() const;
@ -182,6 +186,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlRectFValueType
QML_FOREIGN(QRectF) QML_FOREIGN(QRectF)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlRectFValueType) QML_EXTENDED(QQmlRectFValueType)
QML_STRUCTURED_VALUE
public: public:
Q_INVOKABLE QString toString() const; Q_INVOKABLE QString toString() const;
@ -217,6 +222,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlRectValueType
QML_FOREIGN(QRect) QML_FOREIGN(QRect)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlRectValueType) QML_EXTENDED(QQmlRectValueType)
QML_STRUCTURED_VALUE
public: public:
Q_INVOKABLE QString toString() const; Q_INVOKABLE QString toString() const;
@ -282,6 +288,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlEasingValueType
QML_FOREIGN(QEasingCurve) QML_FOREIGN(QEasingCurve)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQmlEasingValueType) QML_EXTENDED(QQmlEasingValueType)
QML_STRUCTURED_VALUE
Q_PROPERTY(QQmlEasingEnums::Type type READ type WRITE setType FINAL) Q_PROPERTY(QQmlEasingEnums::Type type READ type WRITE setType FINAL)
Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude FINAL) Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude FINAL)

View File

@ -48,8 +48,15 @@ QT_END_NAMESPACE
Q_CLASSINFO("QML.UncreatableReason", REASON) Q_CLASSINFO("QML.UncreatableReason", REASON)
#define QML_VALUE_TYPE(NAME) \ #define QML_VALUE_TYPE(NAME) \
Q_CLASSINFO("QML.Element", #NAME) \ Q_CLASSINFO("QML.Element", #NAME)
QML_UNCREATABLE("Value types cannot be created.")
#define QML_CONSTRUCTIBLE_VALUE \
Q_CLASSINFO("QML.Creatable", "true") \
Q_CLASSINFO("QML.CreationMethod", "construct")
#define QML_STRUCTURED_VALUE \
Q_CLASSINFO("QML.Creatable", "true") \
Q_CLASSINFO("QML.CreationMethod", "structured")
#define QML_SINGLETON \ #define QML_SINGLETON \
Q_CLASSINFO("QML.Singleton", "true") \ Q_CLASSINFO("QML.Singleton", "true") \

View File

@ -52,6 +52,7 @@ struct QPointingDeviceUniqueIdForeign
QML_FOREIGN(QPointingDeviceUniqueId) QML_FOREIGN(QPointingDeviceUniqueId)
QML_VALUE_TYPE(pointingDeviceUniqueId) QML_VALUE_TYPE(pointingDeviceUniqueId)
QML_ADDED_IN_VERSION(2, 9) QML_ADDED_IN_VERSION(2, 9)
QML_UNCREATABLE("pointingDeviceUniqueId cannot be created in QML.")
}; };
#if !QT_CONFIG(quick_animatedimage) #if !QT_CONFIG(quick_animatedimage)

View File

@ -10,6 +10,11 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QQuickColorValueType::QQuickColorValueType(const QString &string)
: v(QColor::fromString(string))
{
}
QVariant QQuickColorValueType::create(const QJSValue &params) QVariant QQuickColorValueType::create(const QJSValue &params)
{ {
return params.isString() ? QColor::fromString(params.toString()) : QVariant(); return params.isString() ? QColor::fromString(params.toString()) : QVariant();

View File

@ -49,10 +49,12 @@ class Q_QUICK_PRIVATE_EXPORT QQuickColorValueType
QML_FOREIGN(QColor) QML_FOREIGN(QColor)
QML_VALUE_TYPE(color) QML_VALUE_TYPE(color)
QML_EXTENDED(QQuickColorValueType) QML_EXTENDED(QQuickColorValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &params); static QVariant create(const QJSValue &params);
Q_INVOKABLE QQuickColorValueType(const QString &string);
Q_INVOKABLE QString toString() const; Q_INVOKABLE QString toString() const;
Q_INVOKABLE QVariant alpha(qreal value) const; Q_INVOKABLE QVariant alpha(qreal value) const;
@ -93,6 +95,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickVector2DValueType
QML_FOREIGN(QVector2D) QML_FOREIGN(QVector2D)
QML_VALUE_TYPE(vector2d) QML_VALUE_TYPE(vector2d)
QML_EXTENDED(QQuickVector2DValueType) QML_EXTENDED(QQuickVector2DValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &params); static QVariant create(const QJSValue &params);
@ -128,6 +131,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickVector3DValueType
QML_FOREIGN(QVector3D) QML_FOREIGN(QVector3D)
QML_VALUE_TYPE(vector3d) QML_VALUE_TYPE(vector3d)
QML_EXTENDED(QQuickVector3DValueType) QML_EXTENDED(QQuickVector3DValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &params); static QVariant create(const QJSValue &params);
@ -168,6 +172,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickVector4DValueType
QML_FOREIGN(QVector4D) QML_FOREIGN(QVector4D)
QML_VALUE_TYPE(vector4d) QML_VALUE_TYPE(vector4d)
QML_EXTENDED(QQuickVector4DValueType) QML_EXTENDED(QQuickVector4DValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &params); static QVariant create(const QJSValue &params);
@ -209,6 +214,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickQuaternionValueType
QML_FOREIGN(QQuaternion) QML_FOREIGN(QQuaternion)
QML_VALUE_TYPE(quaternion) QML_VALUE_TYPE(quaternion)
QML_EXTENDED(QQuickQuaternionValueType) QML_EXTENDED(QQuickQuaternionValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &params); static QVariant create(const QJSValue &params);
@ -267,6 +273,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMatrix4x4ValueType
QML_FOREIGN(QMatrix4x4) QML_FOREIGN(QMatrix4x4)
QML_VALUE_TYPE(matrix4x4) QML_VALUE_TYPE(matrix4x4)
QML_EXTENDED(QQuickMatrix4x4ValueType) QML_EXTENDED(QQuickMatrix4x4ValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &params); static QVariant create(const QJSValue &params);
@ -390,6 +397,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFontValueType
QML_FOREIGN(QFont) QML_FOREIGN(QFont)
QML_ADDED_IN_VERSION(2, 0) QML_ADDED_IN_VERSION(2, 0)
QML_EXTENDED(QQuickFontValueType) QML_EXTENDED(QQuickFontValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &value); static QVariant create(const QJSValue &value);
@ -494,6 +502,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickColorSpaceValueType
QML_FOREIGN(QColorSpace) QML_FOREIGN(QColorSpace)
QML_ADDED_IN_VERSION(2, 15) QML_ADDED_IN_VERSION(2, 15)
QML_EXTENDED(QQuickColorSpaceValueType) QML_EXTENDED(QQuickColorSpaceValueType)
QML_STRUCTURED_VALUE
public: public:
static QVariant create(const QJSValue &params); static QVariant create(const QJSValue &params);

View File

@ -0,0 +1,38 @@
import QtQml
import Test
MyTypeObject {
property point p: ({x: 7, y: 77, notthere: 2})
property size s: ({width: 7, height: "77"})
property rect r: ({x: 5, y: 55, width: 7, height: 77})
property point p2
property size s2
property rect r2
property constructible c1: 5
property constructible c2: ({foo: 7})
property constructible c3
property constructible c4
property list<point> ps: [{x: 1, y: 2}, {x: 3, y: 4}, {x: 55, y: Qt.locale()}]
property list<size> ss: [{width: 5, height: 6}, {width: 7, height: 8}, {height: 99}]
property list<constructible> cs: [1, 2, 3, 4, 5, {}]
property structured b1: ({i: 10, c: 14, p: {x: 1, y: 44} })
property structured b2
property list<structured> bb: [{i : 21}, {c: 22}, {p: {x: 199, y: 222}}]
constructible: 47
structured: ({i: 11, c: 12, p: {x: 7, y: 8}})
Component.onCompleted: {
p2 = {x: 4, y: 5};
s2 = {width: 7, height: 8};
r2 = {x: 9, y: 10, width: 11, height: 12};
c3 = 99;
b2 = {i: 11, c: 15, p: {x: 4} }
c4 = {foo: 11};
}
}

View File

@ -5,4 +5,6 @@
void registerTypes() void registerTypes()
{ {
qmlRegisterType<MyTypeObject>("Test", 1, 0, "MyTypeObject"); qmlRegisterType<MyTypeObject>("Test", 1, 0, "MyTypeObject");
qmlRegisterTypesAndRevisions<ConstructibleValueType>("Test", 1);
qmlRegisterTypesAndRevisions<StructuredValueType>("Test", 1);
} }

View File

@ -19,6 +19,61 @@
#include <QColor> #include <QColor>
#include <qqml.h> #include <qqml.h>
struct ConstructibleValueType
{
Q_GADGET
Q_PROPERTY(int foo MEMBER m_foo CONSTANT)
QML_VALUE_TYPE(constructible)
QML_CONSTRUCTIBLE_VALUE
public:
ConstructibleValueType() = default;
Q_INVOKABLE ConstructibleValueType(int foo) : m_foo(foo) {}
int foo() const { return m_foo; }
private:
friend bool operator==(const ConstructibleValueType &a, const ConstructibleValueType &b)
{
return a.m_foo == b.m_foo;
}
int m_foo = 0;
};
struct StructuredValueType
{
Q_GADGET
Q_PROPERTY(int i READ i WRITE setI)
Q_PROPERTY(ConstructibleValueType c READ c WRITE setC)
Q_PROPERTY(QPointF p READ p WRITE setP)
QML_VALUE_TYPE(structured)
QML_STRUCTURED_VALUE
public:
int i() const { return m_i; }
void setI(int newI) { m_i = newI; }
const ConstructibleValueType &c() const { return m_c; }
void setC(const ConstructibleValueType &newC) { m_c = newC; }
QPointF p() const { return m_p; }
void setP(QPointF newP) { m_p = newP; }
private:
friend bool operator==(const StructuredValueType &a, const StructuredValueType &b)
{
return a.m_i == b.m_i && a.m_c == b.m_c && a.m_p == b.m_p;
}
int m_i = 0;
ConstructibleValueType m_c;
QPointF m_p;
};
class MyTypeObject : public QObject class MyTypeObject : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -41,6 +96,8 @@ class MyTypeObject : public QObject
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY changed) Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY changed)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed)
Q_PROPERTY(QVariant variant READ variant NOTIFY changed) Q_PROPERTY(QVariant variant READ variant NOTIFY changed)
Q_PROPERTY(ConstructibleValueType constructible READ constructible WRITE setConstructible NOTIFY constructibleChanged)
Q_PROPERTY(StructuredValueType structured READ structured WRITE setStructured NOTIFY structuredChanged)
public: public:
MyTypeObject() : MyTypeObject() :
@ -144,12 +201,36 @@ public:
void emitRunScript() { emit runScript(); } void emitRunScript() { emit runScript(); }
const ConstructibleValueType &constructible() const { return m_constructible; }
void setConstructible(const ConstructibleValueType &newConstructible)
{
if (m_constructible == newConstructible)
return;
m_constructible = newConstructible;
emit constructibleChanged();
}
const StructuredValueType &structured() const { return m_structured; }
void setStructured(const StructuredValueType &newStructured)
{
if (m_structured == newStructured)
return;
m_structured = newStructured;
emit structuredChanged();
}
signals: signals:
void changed(); void changed();
void runScript(); void runScript();
void constructibleChanged();
void structuredChanged();
public slots: public slots:
QSize method() { return QSize(13, 14); } QSize method() { return QSize(13, 14); }
private:
ConstructibleValueType m_constructible;
StructuredValueType m_structured;
}; };
void registerTypes(); void registerTypes();

View File

@ -38,6 +38,7 @@ private slots:
void invokableFunctions(); void invokableFunctions();
void userType(); void userType();
void changedSignal(); void changedSignal();
void structured();
}; };
void tst_qqmlvaluetypeproviders::initTestCase() void tst_qqmlvaluetypeproviders::initTestCase()
@ -245,6 +246,95 @@ void tst_qqmlvaluetypeproviders::changedSignal()
QVERIFY(object->property("success").toBool()); QVERIFY(object->property("success").toBool());
} }
void tst_qqmlvaluetypeproviders::structured()
{
QQmlEngine e;
const QUrl url = testFileUrl("structuredValueTypes.qml");
QQmlComponent component(&e, url);
QVERIFY2(!component.isError(), qPrintable(component.errorString()));
const char *warnings[] = {
"Could not find any constructor for value type ConstructibleValueType to call"
" with value [object Object]",
"Could not find any constructor for value type ConstructibleValueType to call"
" with value QVariant(QJSValue, )",
"Could not convert [object Object] to double for property y",
"Could not find any constructor for value type ConstructibleValueType to call"
" with value [object Object]",
"Could not find any constructor for value type ConstructibleValueType to call"
" with value QVariant(QVariantMap, QMap())",
"Could not convert array value at position 5 from QVariantMap to ConstructibleValueType",
"Could not find any constructor for value type ConstructibleValueType to call"
" with value [object Object]",
"Could not find any constructor for value type ConstructibleValueType to call"
" with value QVariant(QJSValue, )"
};
for (const auto warning : warnings)
QTest::ignoreMessage(QtWarningMsg, warning);
QTest::ignoreMessage(QtWarningMsg, qPrintable(
url.toString() + QStringLiteral(":36: Error: Cannot assign QJSValue "
"to ConstructibleValueType")));
QTest::ignoreMessage(QtWarningMsg, qPrintable(
url.toString() + QStringLiteral(":14:5: Unable to assign QJSValue "
"to ConstructibleValueType")));
QScopedPointer<QObject> o(component.create());
QVERIFY2(!o.isNull(), qPrintable(component.errorString()));
QCOMPARE(o->property("p").value<QPointF>(), QPointF(7, 77));
QCOMPARE(o->property("s").value<QSizeF>(), QSizeF(7, 77));
QCOMPARE(o->property("r").value<QRectF>(), QRectF(5, 55, 7, 77));
QCOMPARE(o->property("p2").value<QPointF>(), QPointF(4, 5));
QCOMPARE(o->property("s2").value<QSizeF>(), QSizeF(7, 8));
QCOMPARE(o->property("r2").value<QRectF>(), QRectF(9, 10, 11, 12));
QCOMPARE(o->property("c1").value<ConstructibleValueType>(), ConstructibleValueType(5));
QCOMPARE(o->property("c2").value<ConstructibleValueType>(), ConstructibleValueType(0));
QCOMPARE(o->property("c3").value<ConstructibleValueType>(), ConstructibleValueType(99));
QCOMPARE(o->property("c4").value<ConstructibleValueType>(), ConstructibleValueType(0));
QCOMPARE(o->property("ps").value<QList<QPointF>>(),
QList<QPointF>({QPointF(1, 2), QPointF(3, 4), QPointF(55, 0)}));
QCOMPARE(o->property("ss").value<QList<QSizeF>>(),
QList<QSizeF>({QSizeF(5, 6), QSizeF(7, 8), QSizeF(-1, 99)}));
QCOMPARE(o->property("cs").value<QList<ConstructibleValueType>>(),
QList<ConstructibleValueType>({1, 2, 3, 4, 5, 0}));
StructuredValueType b1;
b1.setI(10);
b1.setC(14);
b1.setP(QPointF(1, 44));
QCOMPARE(o->property("b1").value<StructuredValueType>(), b1);
StructuredValueType b2;
b2.setI(11);
b2.setC(15);
b2.setP(QPointF(4, 0));
QCOMPARE(o->property("b2").value<StructuredValueType>(), b2);
QList<StructuredValueType> bb(3);
bb[0].setI(21);
bb[1].setC(22);
bb[2].setP(QPointF(199, 222));
QCOMPARE(o->property("bb").value<QList<StructuredValueType>>(), bb);
MyTypeObject *t = qobject_cast<MyTypeObject *>(o.data());
QVERIFY(t);
QCOMPARE(t->constructible(), ConstructibleValueType(47));
StructuredValueType structured;
structured.setI(11);
structured.setC(12);
structured.setP(QPointF(7, 8));
QCOMPARE(t->structured(), structured);
}
QTEST_MAIN(tst_qqmlvaluetypeproviders) QTEST_MAIN(tst_qqmlvaluetypeproviders)
#include "tst_qqmlvaluetypeproviders.moc" #include "tst_qqmlvaluetypeproviders.moc"