aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc27
-rw-r--r--src/qml/jsapi/qjsengine.cpp7
-rw-r--r--src/qml/jsapi/qjsengine_p.h5
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp3
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp13
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h1
-rw-r--r--src/qml/qml/v8/qv8engine.cpp18
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/customvaluetype.qml8
-rw-r--r--tests/auto/qml/qqmlvaluetypes/qqmlvaluetypes.pro3
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp83
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"