("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
Creates a QJSValue with the given \a value.
- \sa fromScriptValue()
+ \sa fromScriptValue(), newVariant()
*/
/*! \fn T QJSEngine::fromScriptValue(const QJSValue &value)
*/
+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)
class QQmlPropertyCache;
+namespace QV4 {
+struct ExecutionEngine;
+}
+
class QJSEnginePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QJSEngine)
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();
break;
}
+ QMetaType metaType(t);
+ if (metaType.flags() & QMetaType::IsGadget)
+ return metaType.metaObject();
return 0;
}
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;
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();
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);
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);
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);
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();
;
}
+ {
+ 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);
--- /dev/null
+import QtQuick 2.0
+import Test 1.0
+
+TypeWithCustomValueType {
+ desk {
+ monitorCount: 3
+ }
+}
QT += core-private gui-private qml-private quick-private gui testlib
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+
+DISTFILES += \
+ data/customvaluetype.qml
void initializeByWrite();
void groupedInterceptors();
void groupedInterceptors_data();
+ void customValueType();
+ void customValueTypeInQml();
private:
QQmlEngine engine;
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"