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:
parent
b101be9be6
commit
3195b44e1c
|
@ -1302,3 +1302,98 @@
|
|||
|
||||
\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
|
||||
*/
|
||||
|
|
|
@ -1580,7 +1580,13 @@ static QVariant toVariant(
|
|||
QV4::ScopedValue arrayValue(scope);
|
||||
for (qint64 i = 0; i < length; ++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)) {
|
||||
// before attempting a conversion from the concrete types,
|
||||
// check if there exists a conversion from QJSValue -> out type
|
||||
|
@ -1600,10 +1606,12 @@ static QVariant toVariant(
|
|||
auto originalType = asVariant.metaType();
|
||||
bool couldConvert = asVariant.convert(valueMetaType);
|
||||
if (!couldConvert) {
|
||||
qWarning() << 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()));
|
||||
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()));
|
||||
// create default constructed value
|
||||
asVariant = QVariant(valueMetaType, nullptr);
|
||||
}
|
||||
|
@ -1662,6 +1670,12 @@ static QVariant toVariant(
|
|||
return re->toQRegularExpression();
|
||||
#endif
|
||||
|
||||
if (metaType.isValid() && !(metaType.flags() & QMetaType::PointerToQObject)) {
|
||||
QVariant result(metaType);
|
||||
if (QQmlValueTypeProvider::createValueType(value, metaType, result.data()))
|
||||
return result;
|
||||
}
|
||||
|
||||
if (createJSValueForObjects)
|
||||
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
|
||||
|
||||
|
@ -2504,8 +2518,17 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
|
|||
}
|
||||
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:
|
||||
;
|
||||
break;
|
||||
}
|
||||
|
||||
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());
|
||||
return true;
|
||||
} else if (!isPointer) {
|
||||
QVariant val;
|
||||
if (QQmlValueTypeProvider::createValueType(
|
||||
metaType, QJSValuePrivate::fromReturnedValue(value.asReturnedValue()), val)) {
|
||||
Q_ASSERT(val.metaType() == metaType);
|
||||
metaType.destruct(data);
|
||||
metaType.construct(data, val.constData());
|
||||
if (QQmlValueTypeProvider::createValueType(value, metaType, data))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (const QV4::Sequence *sequence = value.as<Sequence>()) {
|
||||
|
|
|
@ -648,12 +648,20 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
|
|||
meta->addValueAtEnd(result.data(), &variant);
|
||||
} else {
|
||||
const QMetaType originalType = variant.metaType();
|
||||
if (originalType != valueMetaType && !variant.convert(valueMetaType)) {
|
||||
qWarning() << 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 = QVariant(valueMetaType);
|
||||
if (originalType != valueMetaType) {
|
||||
QVariant converted(valueMetaType);
|
||||
if (QQmlValueTypeProvider::createValueType(
|
||||
variant, valueMetaType, converted.data())) {
|
||||
variant = converted;
|
||||
} 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());
|
||||
}
|
||||
|
|
|
@ -166,7 +166,8 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::zero(),
|
||||
-1
|
||||
-1,
|
||||
QQmlPrivate::ValueTypeCreationMethod::None
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -464,11 +465,19 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
|
|||
&& boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
|
||||
|
||||
QString noCreateReason;
|
||||
ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
|
||||
|
||||
if (!creatable) {
|
||||
noCreateReason = QString::fromUtf8(classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
|
||||
noCreateReason = QString::fromUtf8(
|
||||
classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
|
||||
if (noCreateReason.isEmpty())
|
||||
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 = {
|
||||
|
@ -493,7 +502,8 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
|
|||
type.extensionMetaObject,
|
||||
nullptr,
|
||||
QTypeRevision(),
|
||||
type.structVersion > 0 ? type.finalizerCast : -1
|
||||
type.structVersion > 0 ? type.finalizerCast : -1,
|
||||
creationMethod
|
||||
};
|
||||
|
||||
QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
|
||||
|
|
|
@ -78,7 +78,8 @@ int qmlRegisterAnonymousType(const char *uri, int versionMajor)
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -115,7 +116,8 @@ int qmlRegisterAnonymousType(const char *uri, int versionMajor)
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::fromMinorVersion(metaObjectRevisionMinor),
|
||||
QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -160,7 +162,8 @@ int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMin
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -193,7 +196,8 @@ int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMin
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::fromMinorVersion(metaObjectRevision),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -233,7 +237,8 @@ int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int ve
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -273,7 +278,8 @@ int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int ve
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::fromMinorVersion(metaObjectRevision),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -306,7 +312,8 @@ int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const c
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -337,7 +344,8 @@ int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const c
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::fromMinorVersion(metaObjectRevision),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -368,7 +376,8 @@ int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::fromMinorVersion(metaObjectRevision),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -401,7 +410,8 @@ int qmlRegisterExtendedType(const char *uri, int versionMajor)
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -440,7 +450,8 @@ int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor,
|
|||
|
||||
nullptr,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -490,7 +501,8 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
|
|||
|
||||
parser,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -522,7 +534,8 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
|
|||
|
||||
parser,
|
||||
QTypeRevision::fromMinorVersion(metaObjectRevision),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
@ -561,7 +574,8 @@ int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int version
|
|||
|
||||
parser,
|
||||
QTypeRevision::zero(),
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast()
|
||||
QQmlPrivate::StaticCastSelector<T,QQmlFinalizerHook>::cast(),
|
||||
QQmlPrivate::ValueTypeCreationMethod::None,
|
||||
};
|
||||
|
||||
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
||||
|
|
|
@ -457,11 +457,9 @@ QVariant QtObject::font(const QJSValue &fontSpecifier) const
|
|||
}
|
||||
|
||||
{
|
||||
QVariant v;
|
||||
if (QQmlValueTypeProvider::createValueType(
|
||||
QMetaType(QMetaType::QFont), fontSpecifier, v)) {
|
||||
QVariant v((QMetaType(QMetaType::QFont)));
|
||||
if (QQmlValueTypeProvider::constructFromJSValue(fontSpecifier, v.metaType(), v.data()))
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
static QVariant createValueType(QJSEngine *e, QMetaType type, T... parameters)
|
||||
static QVariant constructFromJSValue(QJSEngine *e, QMetaType type, T... parameters)
|
||||
{
|
||||
if (!e)
|
||||
return QVariant();
|
||||
QJSValue params = e->newArray(sizeof...(parameters));
|
||||
addParameters(e, params, 0, parameters...);
|
||||
QVariant variant;
|
||||
QQmlValueTypeProvider::createValueType(type, params, variant);
|
||||
QVariant variant(type);
|
||||
QQmlValueTypeProvider::constructFromJSValue(params, type, variant.data());
|
||||
return variant;
|
||||
}
|
||||
|
||||
|
@ -507,7 +505,7 @@ static QVariant createValueType(QJSEngine *e, QMetaType type, T... parameters)
|
|||
*/
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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 variant;
|
||||
QQmlValueTypeProvider::createValueType(QMetaType(QMetaType::QMatrix4x4), QJSValue(), variant);
|
||||
QVariant variant((QMetaType(QMetaType::QMatrix4x4)));
|
||||
QQmlValueTypeProvider::constructFromJSValue(
|
||||
QJSValue(), variant.metaType(), variant.data());
|
||||
return variant;
|
||||
}
|
||||
|
||||
QVariant QtObject::matrix4x4(const QJSValue &value) const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
QVariant v;
|
||||
if (QQmlValueTypeProvider::createValueType(QMetaType(QMetaType::QMatrix4x4), value, v))
|
||||
QVariant v((QMetaType(QMetaType::QMatrix4x4)));
|
||||
if (QQmlValueTypeProvider::constructFromJSValue(value, v.metaType(), v.data()))
|
||||
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 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,
|
||||
m31, m32, m33, m34, m41, m42, m43, m44);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <private/qqmlglobal_p.h>
|
||||
#include <QtQml/private/qqmlmetatype_p.h>
|
||||
#include <QtQml/private/qjsvalue_p.h>
|
||||
|
||||
#include <QtQml/qqmlengine.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
|
@ -12,13 +13,237 @@
|
|||
|
||||
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);
|
||||
if (auto valueTypeFunction = qmlType.createValueTypeFunction()) {
|
||||
switch (metaType.id()) {
|
||||
// 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);
|
||||
if (result.metaType() == metaType) {
|
||||
data = std::move(result);
|
||||
metaType.destruct(data);
|
||||
metaType.construct(data, result.constData());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +251,98 @@ bool QQmlValueTypeProvider::createValueType(QMetaType metaType, const QJSValue &
|
|||
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() {}
|
||||
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; }
|
||||
|
|
|
@ -185,7 +185,12 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent)
|
|||
class QQmlValueTypeProvider
|
||||
{
|
||||
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
|
||||
|
|
|
@ -152,6 +152,10 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
|
|||
d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
|
||||
d->extraData.cd->registerEnumClassesUnscoped = 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)
|
||||
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
|
||||
// the implicit use of gadgets.
|
||||
if (!(metaType.flags() & QMetaType::PointerToQObject)) {
|
||||
const QQmlType qmlType = QQmlMetaType::qmlType(metaType);
|
||||
|
||||
// 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 (const QMetaObject *mo = metaObjectForValueType(QQmlMetaType::qmlType(metaType)))
|
||||
return mo;
|
||||
}
|
||||
|
||||
// If it _is_ a gadget, we can just use it.
|
||||
|
|
|
@ -240,6 +240,26 @@ public:
|
|||
static bool isValueType(QMetaType type);
|
||||
static QQmlValueType *valueType(QMetaType metaType);
|
||||
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 void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
|
||||
|
|
|
@ -425,9 +425,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
|
|||
}
|
||||
break;
|
||||
case QMetaType::QColor: {
|
||||
QVariant data;
|
||||
QVariant data(propertyType);
|
||||
if (QQmlValueTypeProvider::createValueType(
|
||||
propertyType, compilationUnit->bindingValueAsString(binding), data)) {
|
||||
compilationUnit->bindingValueAsString(binding), propertyType, data.data())) {
|
||||
property->writeProperty(_qobject, data.data(), propertyWriteFlags);
|
||||
}
|
||||
}
|
||||
|
@ -508,9 +508,10 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
|
|||
case QMetaType::QVector3D:
|
||||
case QMetaType::QVector4D:
|
||||
case QMetaType::QQuaternion: {
|
||||
QVariant result;
|
||||
QVariant result(propertyType);
|
||||
bool ok = QQmlValueTypeProvider::createValueType(
|
||||
propertyType, compilationUnit->bindingValueAsString(binding), result);
|
||||
compilationUnit->bindingValueAsString(binding),
|
||||
result.metaType(), result.data());
|
||||
assertOrNull(ok);
|
||||
Q_UNUSED(ok);
|
||||
property->writeProperty(_qobject, result.data(), propertyWriteFlags);
|
||||
|
@ -576,6 +577,35 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
|
|||
}
|
||||
property->writeProperty(_qobject, &value, propertyWriteFlags);
|
||||
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
|
||||
|
|
|
@ -427,11 +427,14 @@ namespace QQmlPrivate
|
|||
enum AutoParentResult { Parented, IncompatibleObject, IncompatibleParent };
|
||||
typedef AutoParentResult (*AutoParentFunction)(QObject *object, QObject *parent);
|
||||
|
||||
enum class ValueTypeCreationMethod { None, Construct, Structured };
|
||||
|
||||
struct RegisterType {
|
||||
enum StructVersion: int {
|
||||
Base = 0,
|
||||
FinalizerCast = 1,
|
||||
CurrentVersion = FinalizerCast,
|
||||
CreationMethod = 2,
|
||||
CurrentVersion = CreationMethod,
|
||||
};
|
||||
|
||||
bool has(StructVersion v) const { return structVersion >= int(v); }
|
||||
|
@ -446,6 +449,7 @@ namespace QQmlPrivate
|
|||
void *userdata;
|
||||
QString noCreationReason;
|
||||
|
||||
// ### Qt7: Get rid of this. It can be covered by creationMethod below.
|
||||
QVariant (*createValueType)(const QJSValue &);
|
||||
|
||||
const char *uri;
|
||||
|
@ -467,6 +471,8 @@ namespace QQmlPrivate
|
|||
|
||||
QTypeRevision revision;
|
||||
int finalizerCast;
|
||||
|
||||
ValueTypeCreationMethod creationMethod;
|
||||
// If this is extended ensure "version" is bumped!!!
|
||||
};
|
||||
|
||||
|
|
|
@ -1220,7 +1220,7 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx,
|
|||
return false;
|
||||
|
||||
QVariant v = value;
|
||||
if (prop.isEnumType()) {
|
||||
if (prop.isEnumType() && v.metaType() != prop.metaType()) {
|
||||
QMetaEnum menum = prop.enumerator();
|
||||
if (v.userType() == QMetaType::QString) {
|
||||
bool ok;
|
||||
|
@ -1360,6 +1360,80 @@ private:
|
|||
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(
|
||||
QObject *object, const QQmlPropertyData &property, const QVariant &value,
|
||||
const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
|
||||
|
@ -1419,37 +1493,9 @@ bool QQmlPropertyPrivate::write(
|
|||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (value.canConvert(propertyMetaType)
|
||||
&& !isUrl && variantMetaType != QMetaType::fromType<QString>()
|
||||
&& propertyMetaType != QMetaType::fromType<QList<QUrl>>() && !property.isQList()) {
|
||||
// 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 (ConvertAndAssignResult result = tryConvertAndAssign(
|
||||
object, property, value, flags, propertyMetaType, variantMetaType, isUrl)) {
|
||||
return result.couldWrite;
|
||||
} else if (propertyMetaType == QMetaType::fromType<QVariant>()) {
|
||||
return property.writeProperty(object, const_cast<QVariant *>(&value), flags);
|
||||
} else if (isUrl) {
|
||||
|
|
|
@ -568,10 +568,10 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
|
|||
default: return QString();
|
||||
}
|
||||
};
|
||||
QVariant result;
|
||||
QVariant result(property->propType());
|
||||
if (!QQmlValueTypeProvider::createValueType(
|
||||
property->propType(),
|
||||
compilationUnit->bindingValueAsString(binding), result)) {
|
||||
compilationUnit->bindingValueAsString(binding),
|
||||
result.metaType(), result.data())) {
|
||||
return warnOrError(tr("Invalid property assignment: %1 expected")
|
||||
.arg(typeName()));
|
||||
}
|
||||
|
@ -618,6 +618,8 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding(
|
|||
} else if (property->isQObject()
|
||||
&& bindingType == QV4::CompiledData::Binding::Type_Null) {
|
||||
break;
|
||||
} else if (QQmlMetaType::qmlType(property->propType()).canConstructValueType()) {
|
||||
break;
|
||||
}
|
||||
|
||||
return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(property->propType().name())));
|
||||
|
|
|
@ -40,11 +40,15 @@ QVariant QQmlStringConverters::variantFromString(const QString &s, QMetaType pre
|
|||
case QMetaType::QRect:
|
||||
return QVariant::fromValue(rectFFromString(s, ok).toRect());
|
||||
default: {
|
||||
QVariant ret;
|
||||
bool success = QQmlValueTypeProvider::createValueType(preferredType, QJSValue(s), ret);
|
||||
QVariant ret(preferredType);
|
||||
if (QQmlValueTypeProvider::createValueType(s, preferredType, ret.data())) {
|
||||
if (ok)
|
||||
*ok = true;
|
||||
return ret;
|
||||
}
|
||||
if (ok)
|
||||
*ok = success;
|
||||
return ret;
|
||||
*ok = false;
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -527,6 +527,20 @@ QQmlType::CreateValueTypeFunc QQmlType::createValueTypeFunction() const
|
|||
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
|
||||
{
|
||||
if (!d || d->regType != CppType)
|
||||
|
|
|
@ -71,6 +71,9 @@ public:
|
|||
typedef QVariant (*CreateValueTypeFunc)(const QJSValue &);
|
||||
CreateValueTypeFunc createValueTypeFunction() const;
|
||||
|
||||
bool canConstructValueType() const;
|
||||
bool canPopulateValueType() const;
|
||||
|
||||
QObject *create() const;
|
||||
QObject *create(void **, size_t) const;
|
||||
QObject *createWithQQmlData() const;
|
||||
|
|
|
@ -92,6 +92,8 @@ public:
|
|||
int finalizerCast;
|
||||
bool registerEnumClassesUnscoped;
|
||||
bool registerEnumsFromRelatedTypes;
|
||||
bool constructValueType;
|
||||
bool populateValueType;
|
||||
};
|
||||
|
||||
struct QQmlSingletonTypeData
|
||||
|
|
|
@ -100,6 +100,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlPointFValueType
|
|||
QML_FOREIGN(QPointF)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQmlPointFValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString toString() const;
|
||||
|
@ -119,6 +120,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlPointValueType
|
|||
QML_FOREIGN(QPoint)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQmlPointValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString toString() const;
|
||||
|
@ -138,6 +140,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlSizeFValueType
|
|||
QML_FOREIGN(QSizeF)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQmlSizeFValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString toString() const;
|
||||
|
@ -157,6 +160,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlSizeValueType
|
|||
QML_FOREIGN(QSize)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQmlSizeValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString toString() const;
|
||||
|
@ -182,6 +186,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlRectFValueType
|
|||
QML_FOREIGN(QRectF)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQmlRectFValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString toString() const;
|
||||
|
@ -217,6 +222,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlRectValueType
|
|||
QML_FOREIGN(QRect)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQmlRectValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString toString() const;
|
||||
|
@ -282,6 +288,7 @@ struct Q_QML_PRIVATE_EXPORT QQmlEasingValueType
|
|||
QML_FOREIGN(QEasingCurve)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQmlEasingValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
Q_PROPERTY(QQmlEasingEnums::Type type READ type WRITE setType FINAL)
|
||||
Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude FINAL)
|
||||
|
|
|
@ -48,8 +48,15 @@ QT_END_NAMESPACE
|
|||
Q_CLASSINFO("QML.UncreatableReason", REASON)
|
||||
|
||||
#define QML_VALUE_TYPE(NAME) \
|
||||
Q_CLASSINFO("QML.Element", #NAME) \
|
||||
QML_UNCREATABLE("Value types cannot be created.")
|
||||
Q_CLASSINFO("QML.Element", #NAME)
|
||||
|
||||
#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 \
|
||||
Q_CLASSINFO("QML.Singleton", "true") \
|
||||
|
|
|
@ -52,6 +52,7 @@ struct QPointingDeviceUniqueIdForeign
|
|||
QML_FOREIGN(QPointingDeviceUniqueId)
|
||||
QML_VALUE_TYPE(pointingDeviceUniqueId)
|
||||
QML_ADDED_IN_VERSION(2, 9)
|
||||
QML_UNCREATABLE("pointingDeviceUniqueId cannot be created in QML.")
|
||||
};
|
||||
|
||||
#if !QT_CONFIG(quick_animatedimage)
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QQuickColorValueType::QQuickColorValueType(const QString &string)
|
||||
: v(QColor::fromString(string))
|
||||
{
|
||||
}
|
||||
|
||||
QVariant QQuickColorValueType::create(const QJSValue ¶ms)
|
||||
{
|
||||
return params.isString() ? QColor::fromString(params.toString()) : QVariant();
|
||||
|
|
|
@ -49,10 +49,12 @@ class Q_QUICK_PRIVATE_EXPORT QQuickColorValueType
|
|||
QML_FOREIGN(QColor)
|
||||
QML_VALUE_TYPE(color)
|
||||
QML_EXTENDED(QQuickColorValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue ¶ms);
|
||||
|
||||
Q_INVOKABLE QQuickColorValueType(const QString &string);
|
||||
Q_INVOKABLE QString toString() const;
|
||||
|
||||
Q_INVOKABLE QVariant alpha(qreal value) const;
|
||||
|
@ -93,6 +95,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickVector2DValueType
|
|||
QML_FOREIGN(QVector2D)
|
||||
QML_VALUE_TYPE(vector2d)
|
||||
QML_EXTENDED(QQuickVector2DValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue ¶ms);
|
||||
|
@ -128,6 +131,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickVector3DValueType
|
|||
QML_FOREIGN(QVector3D)
|
||||
QML_VALUE_TYPE(vector3d)
|
||||
QML_EXTENDED(QQuickVector3DValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue ¶ms);
|
||||
|
@ -168,6 +172,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickVector4DValueType
|
|||
QML_FOREIGN(QVector4D)
|
||||
QML_VALUE_TYPE(vector4d)
|
||||
QML_EXTENDED(QQuickVector4DValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue ¶ms);
|
||||
|
@ -209,6 +214,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickQuaternionValueType
|
|||
QML_FOREIGN(QQuaternion)
|
||||
QML_VALUE_TYPE(quaternion)
|
||||
QML_EXTENDED(QQuickQuaternionValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue ¶ms);
|
||||
|
@ -267,6 +273,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMatrix4x4ValueType
|
|||
QML_FOREIGN(QMatrix4x4)
|
||||
QML_VALUE_TYPE(matrix4x4)
|
||||
QML_EXTENDED(QQuickMatrix4x4ValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue ¶ms);
|
||||
|
@ -390,6 +397,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFontValueType
|
|||
QML_FOREIGN(QFont)
|
||||
QML_ADDED_IN_VERSION(2, 0)
|
||||
QML_EXTENDED(QQuickFontValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue &value);
|
||||
|
@ -494,6 +502,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickColorSpaceValueType
|
|||
QML_FOREIGN(QColorSpace)
|
||||
QML_ADDED_IN_VERSION(2, 15)
|
||||
QML_EXTENDED(QQuickColorSpaceValueType)
|
||||
QML_STRUCTURED_VALUE
|
||||
|
||||
public:
|
||||
static QVariant create(const QJSValue ¶ms);
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
}
|
|
@ -5,4 +5,6 @@
|
|||
void registerTypes()
|
||||
{
|
||||
qmlRegisterType<MyTypeObject>("Test", 1, 0, "MyTypeObject");
|
||||
qmlRegisterTypesAndRevisions<ConstructibleValueType>("Test", 1);
|
||||
qmlRegisterTypesAndRevisions<StructuredValueType>("Test", 1);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,61 @@
|
|||
#include <QColor>
|
||||
#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
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -41,6 +96,8 @@ class MyTypeObject : public QObject
|
|||
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY changed)
|
||||
Q_PROPERTY(QColor color READ color WRITE setColor 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:
|
||||
MyTypeObject() :
|
||||
|
@ -144,12 +201,36 @@ public:
|
|||
|
||||
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:
|
||||
void changed();
|
||||
void runScript();
|
||||
|
||||
void constructibleChanged();
|
||||
void structuredChanged();
|
||||
|
||||
public slots:
|
||||
QSize method() { return QSize(13, 14); }
|
||||
private:
|
||||
ConstructibleValueType m_constructible;
|
||||
StructuredValueType m_structured;
|
||||
};
|
||||
|
||||
void registerTypes();
|
||||
|
|
|
@ -38,6 +38,7 @@ private slots:
|
|||
void invokableFunctions();
|
||||
void userType();
|
||||
void changedSignal();
|
||||
void structured();
|
||||
};
|
||||
|
||||
void tst_qqmlvaluetypeproviders::initTestCase()
|
||||
|
@ -245,6 +246,95 @@ void tst_qqmlvaluetypeproviders::changedSignal()
|
|||
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)
|
||||
|
||||
#include "tst_qqmlvaluetypeproviders.moc"
|
||||
|
|
Loading…
Reference in New Issue