aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2014-07-15 10:35:24 +0200
committerSean Harmer <sean.harmer@kdab.com>2014-07-25 16:29:36 +0200
commitfcb40ff6d71f4561401e6b2bd4d7fc706fff8eee (patch)
tree942096e3c60e7d08349fa5c1491620e6101146f3
parent38149bff7c4b89b59f73f4f4d6d5bd1ce9dafbc1 (diff)
qmlRegisterCustomExtendedType, qmlRegisterExtendedUncreatableType
There are cases in Qt3D where we'd like to be able to register uncreatable extended types. The main use case is having an abstract class that has an extension class so as to have a clean separation between the C++ and QML API. Implementations of the abstract can then be easily registered to QML and rely on the extension class for QML specific properties. The other feature we'll need in the near future is the ability to create extended QML types that use a custom parser. Two new type registration method were added to qqml.h to fulfill those needs. Unit tests for those are present in qqmlecmascript and qqmllanguage. Change-Id: I15b2cd791ffd36b537305af1873491c079d4094e Reviewed-by: Sean Harmer <sean.harmer@kdab.com> Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc38
-rw-r--r--src/qml/qml/qqml.h77
-rw-r--r--tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup3.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/data/uncreatableExtendedObjectFailureCheck.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.cpp41
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp34
-rw-r--r--tests/auto/qml/qqmllanguage/data/customExtendedParserProperties.qml12
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h19
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp19
10 files changed, 263 insertions, 0 deletions
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index d3d3174193..e95784dc5c 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -157,6 +157,44 @@
*/
/*!
+ \fn int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
+ \relates QQmlEgine
+
+ This template function registers the C++ type and its extension
+ in the QML system with the name \a qmlName in the library imported
+ from \a uri having version number composed from \a versionMajor and
+ \a versionMinor.
+
+ While the type has a name and a type, it cannot be created, and the
+ given error \a message will result if creation is attempted.
+
+ This is useful where the type is only intended for providing attached
+ properties, enum values or an abstract base class with its extension.
+
+ Returns the QML type id.
+
+ #include <QtQml> to use this function.
+
+ \sa qmlRegisterUncreatableType()
+*/
+
+/*!
+ \fn int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, QQmlCustomParser *parser)
+ \relates QQmlEgine
+
+ This template function registers the C++ type and its extension
+ in the QML system with the name \a qmlName in the library imported
+ from \a uri having version number composed from \a versionMajor and
+ \a versionMinor. Properties from the C++ type or its extension that
+ cannot be resolved directly by the QML system will be resolved using
+ the \a parser provided.
+
+ Returns the QML type id.
+
+ #include <QtQml> to use this function.
+*/
+
+/*!
\fn int qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
\relates QQmlEngine
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 721f2cc5a8..86cb395c3d 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -198,6 +198,44 @@ int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMin
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+template<typename T, typename E>
+int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
+{
+ QML_GETTYPENAMES
+
+ QQmlAttachedPropertiesFunc attached = QQmlPrivate::attachedPropertiesFunc<E>();
+ const QMetaObject * attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<E>();
+ if (!attached) {
+ attached = QQmlPrivate::attachedPropertiesFunc<T>();
+ attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<T>();
+ }
+
+ QQmlPrivate::RegisterType type = {
+ 0,
+
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
+ 0, 0,
+ reason,
+
+ uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject,
+
+ attached,
+ attachedMetaObject,
+
+ QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(),
+
+ QQmlPrivate::createParent<E>, &E::staticMetaObject,
+
+ 0,
+ 0
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
@@ -414,6 +452,45 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+template<typename T, typename E>
+int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, QQmlCustomParser *parser)
+{
+ QML_GETTYPENAMES
+
+ QQmlAttachedPropertiesFunc attached = QQmlPrivate::attachedPropertiesFunc<E>();
+ const QMetaObject * attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<E>();
+ if (!attached) {
+ attached = QQmlPrivate::attachedPropertiesFunc<T>();
+ attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<T>();
+ }
+
+ QQmlPrivate::RegisterType type = {
+ 0,
+
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
+ sizeof(T), QQmlPrivate::createInto<T>,
+ QString(),
+
+ uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject,
+
+ attached,
+ attachedMetaObject,
+
+ QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(),
+
+ QQmlPrivate::createParent<E>, &E::staticMetaObject,
+
+ parser,
+ 0
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
class QQmlContext;
class QQmlEngine;
class QJSValue;
diff --git a/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup3.qml b/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup3.qml
new file mode 100644
index 0000000000..ef574f9361
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup3.qml
@@ -0,0 +1,13 @@
+import Qt.test 1.0
+import QtQuick 2.0
+
+QtObject {
+ property ImplementedExtensionObject a : ImplementedExtensionObject { id: root; extendedProperty : 42 }
+ property int b: root.abstractProperty
+ property int c: root.implementedProperty
+ property int d: root.extendedProperty
+
+ function getAbstractProperty() { return b; }
+ function getImplementedProperty() { return c; }
+ function getExtendedProperty() { return d; }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/uncreatableExtendedObjectFailureCheck.qml b/tests/auto/qml/qqmlecmascript/data/uncreatableExtendedObjectFailureCheck.qml
new file mode 100644
index 0000000000..3619d8588c
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/uncreatableExtendedObjectFailureCheck.qml
@@ -0,0 +1,8 @@
+import Qt.test 1.0
+import QtQuick 2.0
+
+QtObject {
+ property AbstractExtensionObject a;
+ a: AbstractExtensionObject { id: root }
+ property int b: Math.max(root.abstractProperty, 0)
+}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp
index eb06b9e57d..56e733a423 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.cpp
+++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp
@@ -63,6 +63,44 @@ private:
int m_value;
};
+class AbstractExtensionObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int abstractProperty READ abstractProperty WRITE setAbstractProperty NOTIFY abstractPropertyChanged)
+public:
+
+ AbstractExtensionObject(QObject *parent = 0) : QObject(parent), m_abstractProperty(-1) {}
+
+ void setAbstractProperty(int abstractProperty) { m_abstractProperty = abstractProperty; emit abstractPropertyChanged(); }
+ int abstractProperty() const { return m_abstractProperty; }
+
+ virtual void shouldBeImplemented() = 0;
+
+signals:
+ void abstractPropertyChanged();
+
+private:
+ int m_abstractProperty;
+};
+
+class ImplementedExtensionObject : public AbstractExtensionObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int implementedProperty READ implementedProperty WRITE setImplementedProperty NOTIFY implementedPropertyChanged)
+public:
+ ImplementedExtensionObject(QObject *parent = 0) : AbstractExtensionObject(parent), m_implementedProperty(883) {}
+ void shouldBeImplemented() {}
+
+ void setImplementedProperty(int implementedProperty) { m_implementedProperty = implementedProperty; emit implementedPropertyChanged(); }
+ int implementedProperty() const { return m_implementedProperty; }
+
+signals:
+ void implementedPropertyChanged();
+
+private:
+ int m_implementedProperty;
+};
+
class ExtensionObject : public QObject
{
Q_OBJECT
@@ -320,6 +358,9 @@ void registerTypes()
qmlRegisterType<MyRevisionedClass,1>("Qt.test",1,1,"MyRevisionedClass");
qmlRegisterType<MyWorkerObject>("Qt.test", 1,0, "MyWorkerObject");
+ qmlRegisterExtendedUncreatableType<AbstractExtensionObject, ExtensionObject>("Qt.test", 1,0, "AbstractExtensionObject", QStringLiteral("Abstracts are uncreatable"));
+ qmlRegisterType<ImplementedExtensionObject>("Qt.test", 1,0, "ImplementedExtensionObject");
+
// test scarce resource property binding post-evaluation optimization
// and for testing memory usage in property var circular reference test
qmlRegisterType<ScarceResourceObject>("Qt.test", 1,0, "MyScarceResourceObject");
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 344645b9cd..d068584797 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -122,6 +122,8 @@ private slots:
void selfDeletingBinding();
void extendedObjectPropertyLookup();
void extendedObjectPropertyLookup2();
+ void uncreatableExtendedObjectFailureCheck();
+ void extendedObjectPropertyLookup3();
void scriptErrors();
void functionErrors();
void propertyAssignmentErrors();
@@ -1833,6 +1835,38 @@ void tst_qqmlecmascript::extendedObjectPropertyLookup2()
delete object;
}
+
+/*
+Test failure when trying to create and uncreatable extended type object.
+ */
+void tst_qqmlecmascript::uncreatableExtendedObjectFailureCheck()
+{
+ QQmlComponent component(&engine, testFileUrl("uncreatableExtendedObjectFailureCheck.qml"));
+
+ QObject *object = component.create();
+ QVERIFY(object == 0);
+}
+
+/*
+Test that an subclass of an uncreatable extended object contains all the required properties.
+ */
+void tst_qqmlecmascript::extendedObjectPropertyLookup3()
+{
+ QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup3.qml"));
+
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+
+ QVariant returnValue;
+ QVERIFY(QMetaObject::invokeMethod(object, "getAbstractProperty", Q_RETURN_ARG(QVariant, returnValue)));
+ QCOMPARE(returnValue.toInt(), -1);
+ QVERIFY(QMetaObject::invokeMethod(object, "getImplementedProperty", Q_RETURN_ARG(QVariant, returnValue)));
+ QCOMPARE(returnValue.toInt(), 883);
+ QVERIFY(QMetaObject::invokeMethod(object, "getExtendedProperty", Q_RETURN_ARG(QVariant, returnValue)));
+ QCOMPARE(returnValue.toInt(), 42);
+
+ delete object;
+}
/*
Test file/lineNumbers for binding/Script errors.
*/
diff --git a/tests/auto/qml/qqmllanguage/data/customExtendedParserProperties.qml b/tests/auto/qml/qqmllanguage/data/customExtendedParserProperties.qml
new file mode 100644
index 0000000000..e53a7c6bd5
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/customExtendedParserProperties.qml
@@ -0,0 +1,12 @@
+import Test 1.0
+import QtQml 2.0
+SimpleExtendedObjectWithCustomParser {
+ id : obj
+ intProperty: 42
+ property string qmlString: "Hello"
+ property var someObject: QtObject {}
+
+ property int c : obj.extendedProperty
+
+ function getExtendedProperty() { return c; }
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index 1498fba9e2..b37c6836eb 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -96,6 +96,8 @@ void registerTypes()
qmlRegisterCustomType<CustomBinding>("Test", 1, 0, "CustomBinding", new CustomBindingParser);
qmlRegisterCustomType<SimpleObjectWithCustomParser>("Test", 1, 0, "SimpleObjectWithCustomParser", new SimpleObjectCustomParser);
+ qmlRegisterCustomExtendedType<SimpleObjectWithCustomParser, SimpleObjectExtension>("Test", 1, 0, "SimpleExtendedObjectWithCustomParser", new SimpleObjectCustomParser);
+
qmlRegisterType<RootObjectInCreationTester>("Test", 1, 0, "RootObjectInCreationTester");
}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index 509cf40e47..bb1e9c158d 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -1144,6 +1144,25 @@ private:
int m_customBindingsCount;
};
+class SimpleObjectExtension : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int extendedProperty READ extendedProperty WRITE setExtendedProperty NOTIFY extendedPropertyChanged)
+public:
+ SimpleObjectExtension(QObject *parent = 0)
+ : QObject(parent)
+ , m_extendedProperty(1584)
+ {}
+
+ void setExtendedProperty(int extendedProperty) { m_extendedProperty = extendedProperty; emit extendedPropertyChanged(); }
+ int extendedProperty() const { return m_extendedProperty; }
+
+signals:
+ void extendedPropertyChanged();
+private:
+ int m_extendedProperty;
+};
+
class SimpleObjectCustomParser : public QQmlCustomParser
{
virtual void verifyBindings(const QV4::CompiledData::QmlUnit *, const QList<const QV4::CompiledData::Binding *> &) {}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index e77c15b79a..6364a8ebdd 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -232,6 +232,7 @@ private slots:
void customParserBindingScopes();
void customParserEvaluateEnum();
void customParserProperties();
+ void customParserWithExtendedObject();
void preservePropertyCacheOnGroupObjects();
void propertyCacheInSync();
@@ -3723,6 +3724,24 @@ void tst_qqmllanguage::customParserProperties()
QVERIFY(!testObject->property("someObject").isNull());
}
+void tst_qqmllanguage::customParserWithExtendedObject()
+{
+ QQmlComponent component(&engine, testFile("customExtendedParserProperties.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+ SimpleObjectWithCustomParser *testObject = qobject_cast<SimpleObjectWithCustomParser*>(o.data());
+ QVERIFY(testObject);
+ QCOMPARE(testObject->customBindingsCount(), 0);
+ QCOMPARE(testObject->intProperty(), 42);
+ QCOMPARE(testObject->property("qmlString").toString(), QStringLiteral("Hello"));
+ QVERIFY(!testObject->property("someObject").isNull());
+
+ QVariant returnValue;
+ QVERIFY(QMetaObject::invokeMethod(o.data(), "getExtendedProperty", Q_RETURN_ARG(QVariant, returnValue)));
+ QCOMPARE(returnValue.toInt(), 1584);
+}
+
void tst_qqmllanguage::preservePropertyCacheOnGroupObjects()
{
QQmlComponent component(&engine, testFile("preservePropertyCacheOnGroupObjects.qml"));