diff options
-rw-r--r-- | src/qml/doc/src/cppintegration/data.qdoc | 27 | ||||
-rw-r--r-- | src/qml/jsapi/qjsengine.cpp | 7 | ||||
-rw-r--r-- | src/qml/jsapi/qjsengine_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetype.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 13 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8engine.cpp | 18 | ||||
-rw-r--r-- | tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml | 8 | ||||
-rw-r--r-- | tests/auto/qml/qqmlvaluetypes/qqmlvaluetypes.pro | 3 | ||||
-rw-r--r-- | tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp | 83 |
10 files changed, 161 insertions, 7 deletions
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index c083d63e51..26d3060318 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -307,6 +307,33 @@ them with default constructed values, do not use the indexed delete operator ("delete sequence[i]") but instead use the \c {splice} function ("sequence.splice(startIndex, deleteCount)"). +\section2 Value types + +Some value types in Qt such as QPoint are represented in JavaScript as objects +that have the same properties and functions like in the C++ API. The same +representation is possible with custom C++ value types. To enable a custom +value type with the QML engine, the class declaration needs to be annotated +with \c{Q_GADGET}. Properties that are intended to be visible in the JavaScript +representation need to be declared with \c Q_PROPERTY. Similarly functions need +to be marked with \c Q_INVOKABLE. This is the same with QObject based C++ APIs. +For example, the \c Actor class below is annotated as gadget and has +properties: + +\code + class Actor + { + Q_GADGET + Q_PROPERTY(QString name READ name WRITE setName) + public: + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; } + + private: + QString m_name; + } + + Q_DECLARE_METATYPE(Actor) +\endcode \section1 Enumeration Types diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 09891019c5..8525aedb6c 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -530,7 +530,7 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) Creates a QJSValue with the given \a value. - \sa fromScriptValue() + \sa fromScriptValue(), newVariant() */ /*! \fn T QJSEngine::fromScriptValue(const QJSValue &value) @@ -541,6 +541,11 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) */ +QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) +{ + return e->v8Engine->publicEngine()->d_func(); +} + QJSEnginePrivate::~QJSEnginePrivate() { for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator iter = propertyCache.begin(); iter != propertyCache.end(); ++iter) diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h index eba8ffb4be..3f7e91bffe 100644 --- a/src/qml/jsapi/qjsengine_p.h +++ b/src/qml/jsapi/qjsengine_p.h @@ -54,6 +54,10 @@ QT_BEGIN_NAMESPACE class QQmlPropertyCache; +namespace QV4 { +struct ExecutionEngine; +} + class QJSEnginePrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QJSEngine) @@ -61,6 +65,7 @@ class QJSEnginePrivate : public QObjectPrivate public: static QJSEnginePrivate* get(QJSEngine*e) { return e->d_func(); } static const QJSEnginePrivate* get(const QJSEngine*e) { return e->d_func(); } + static QJSEnginePrivate* get(QV4::ExecutionEngine *e); QJSEnginePrivate() : mutex(QMutex::Recursive) {} ~QJSEnginePrivate(); diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index e22e0f8fdc..e901a9c0d7 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -107,6 +107,9 @@ const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t) break; } + QMetaType metaType(t); + if (metaType.flags() & QMetaType::IsGadget) + return metaType.metaObject(); return 0; } diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 8ed1169ec7..00b206f0dc 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -133,7 +133,7 @@ bool QQmlValueTypeReference::readReferenceValue() const if (QQmlValueTypeFactory::isValueType(variantReferenceType)) { QQmlPropertyCache *cache = 0; if (const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(variantReferenceType)) - cache = QQmlEnginePrivate::get(engine())->cache(mo); + cache = QJSEnginePrivate::get(engine())->cache(mo); if (d()->gadgetPtr) QMetaType::destroy(d()->metaType, d()->gadgetPtr); d()->gadgetPtr = 0; @@ -177,7 +177,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj ScopedObject proto(scope, engine->qmlExtensions()->valueTypeWrapperPrototype); r->setPrototype(proto); r->d()->object = object; r->d()->property = property; - r->d()->propertyCache = QQmlEnginePrivate::get(engine)->cache(metaObject); + r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject); r->d()->metaType = typeId; r->d()->gadgetPtr = QMetaType::create(r->d()->metaType); return r->asReturnedValue(); @@ -191,7 +191,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->alloc<QQmlValueTypeWrapper>(engine)); ScopedObject proto(scope, engine->qmlExtensions()->valueTypeWrapperPrototype); r->setPrototype(proto); - r->d()->propertyCache = QQmlEnginePrivate::get(engine)->cache(metaObject); + r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject); r->d()->metaType = typeId; r->d()->gadgetPtr = QMetaType::create(r->d()->metaType); r->d()->setValue(value); @@ -206,6 +206,13 @@ QVariant QQmlValueTypeWrapper::toVariant() const return d()->toVariant(); } +void QQmlValueTypeWrapper::toGadget(void *data) const +{ + int typeId = d()->metaType; + QMetaType::destruct(typeId, data); + QMetaType::construct(typeId, data, d()->gadget()); +} + void QQmlValueTypeWrapper::destroy(Heap::Base *that) { Heap::QQmlValueTypeWrapper *w = static_cast<Heap::QQmlValueTypeWrapper *>(that); diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index ad883266bd..2d0fbcbf52 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -84,6 +84,7 @@ public: static ReturnedValue create(ExecutionEngine *engine, const QVariant &, const QMetaObject *metaObject, int typeId); QVariant toVariant() const; + void toGadget(void *data) const; bool isEqual(const QVariant& value); static ReturnedValue get(Managed *m, String *name, bool *hasProperty); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index cf66c5c2f3..0a309c2775 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -700,10 +700,14 @@ QV4::ReturnedValue QV8Engine::metaTypeToJS(int type, const void *data) QByteArray typeName = QMetaType::typeName(type); if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) { return QV4::Encode::null(); - } else { - // Fall back to wrapping in a QVariant. - return QV4::Encode(m_v4Engine->newVariantObject(QVariant(type, data))); } + QMetaType mt(type); + if (mt.flags() & QMetaType::IsGadget) { + Q_ASSERT(mt.metaObject()); + return QV4::QQmlValueTypeWrapper::create(m_v4Engine, QVariant(type, data), mt.metaObject(), type); + } + // Fall back to wrapping in a QVariant. + return QV4::Encode(m_v4Engine->newVariantObject(QVariant(type, data))); } } Q_UNREACHABLE(); @@ -835,6 +839,14 @@ bool QV8Engine::metaTypeFromJS(const QV4::ValueRef value, int type, void *data) ; } + { + QV4::Scoped<QV4::QQmlValueTypeWrapper> vtw(scope, value); + if (vtw && vtw->d()->metaType == type) { + vtw->toGadget(data); + return true; + } + } + #if 0 if (isQtVariant(value)) { const QVariant &var = variantValue(value); diff --git a/tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml b/tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml new file mode 100644 index 0000000000..7ae73475c3 --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import Test 1.0 + +TypeWithCustomValueType { + desk { + monitorCount: 3 + } +} diff --git a/tests/auto/qml/qqmlvaluetypes/qqmlvaluetypes.pro b/tests/auto/qml/qqmlvaluetypes/qqmlvaluetypes.pro index e318966e56..e36be45f41 100644 --- a/tests/auto/qml/qqmlvaluetypes/qqmlvaluetypes.pro +++ b/tests/auto/qml/qqmlvaluetypes/qqmlvaluetypes.pro @@ -15,3 +15,6 @@ CONFIG += parallel_test QT += core-private gui-private qml-private quick-private gui testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + +DISTFILES += \ + data/customvaluetype.qml diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index e3703eac00..f07a34b2f2 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -88,6 +88,8 @@ private slots: void initializeByWrite(); void groupedInterceptors(); void groupedInterceptors_data(); + void customValueType(); + void customValueTypeInQml(); private: QQmlEngine engine; @@ -1422,6 +1424,87 @@ void tst_qqmlvaluetypes::groupedInterceptors() delete object; } +struct MyDesk +{ + Q_PROPERTY(int monitorCount MEMBER monitorCount) + Q_GADGET +public: + MyDesk() : monitorCount(1) {} + + int monitorCount; +}; + +bool operator==(const MyDesk &lhs, const MyDesk &rhs) +{ return lhs.monitorCount == rhs.monitorCount; } +bool operator!=(const MyDesk &lhs, const MyDesk &rhs) +{ return lhs.monitorCount != rhs.monitorCount; } + +Q_DECLARE_METATYPE(MyDesk) + +struct MyOffice +{ + Q_PROPERTY(int chairs MEMBER m_chairs) + Q_PROPERTY(MyDesk desk READ desk WRITE setDesk) + Q_GADGET +public: + MyOffice() : m_chairs(0) {} + + MyDesk desk() const { return m_desk; } + void setDesk(const MyDesk &d) { m_desk = d; } + + int m_chairs; + MyDesk m_desk; +}; + +Q_DECLARE_METATYPE(MyOffice) + +void tst_qqmlvaluetypes::customValueType() +{ + QJSEngine engine; + + MyOffice cppOffice; + cppOffice.m_chairs = 2; + + QJSValue office = engine.toScriptValue(cppOffice); + QCOMPARE(office.property("chairs").toInt(), 2); + office.setProperty("chairs", 1); + QCOMPARE(office.property("chairs").toInt(), 1); + QCOMPARE(cppOffice.m_chairs, 2); + + QJSValue jsDesk = office.property("desk"); + QCOMPARE(jsDesk.property("monitorCount").toInt(), 1); + jsDesk.setProperty("monitorCount", 2); + QCOMPARE(jsDesk.property("monitorCount").toInt(), 2); + + QCOMPARE(cppOffice.desk().monitorCount, 1); + + office.setProperty("desk", jsDesk); + cppOffice = engine.fromScriptValue<MyOffice>(office); + QCOMPARE(cppOffice.m_chairs, 1); + QCOMPARE(cppOffice.desk().monitorCount, 2); +} + +class TypeWithCustomValueType : public QObject +{ + Q_OBJECT + Q_PROPERTY(MyDesk desk MEMBER m_desk) +public: + + MyDesk m_desk; +}; + +void tst_qqmlvaluetypes::customValueTypeInQml() +{ + qmlRegisterType<TypeWithCustomValueType>("Test", 1, 0, "TypeWithCustomValueType"); + QQmlComponent component(&engine, testFileUrl("customvaluetype.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + TypeWithCustomValueType *t = qobject_cast<TypeWithCustomValueType*>(object.data()); + Q_ASSERT(t); + QCOMPARE(t->m_desk.monitorCount, 3); +} + QTEST_MAIN(tst_qqmlvaluetypes) #include "tst_qqmlvaluetypes.moc" |