aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Kelly <stephen.kelly@kdab.com>2012-01-12 20:01:15 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-10 11:17:46 +0200
commit59ed8c355b99df0b949003a438ab850274261aa0 (patch)
tree7f019270e9e8457d385d94c6e8a9b1679b396fb2
parentf20d726766bf5e875f96fc5ce8885afe5c3fae4a (diff)
Make it possible to handle pointers to QObject derived in QML.
This way, properties of QObject derived types can be read in QML code for example: Q_PROPERTY(MyObject* obj READ obj CONSTANT) Previously, only QObject* types could be read by QML: Q_PROPERTY(QObject* obj READ obj CONSTANT) This meant that multiple properties and methods had to be created for classes which were relevant to both QML and non-QML code. This patch lifts that restriction. As a consequence, we can also remove a Q_EXPECT_FAIL from the qqmllanguage unit test. That test was introduced in commit 92562eacbc3c (Allow signal parameters which are custom QML object-types, 2012-07-13) to document knowledge of the limitation while fixing it as much as possible. Task-number: QTBUG-26662 Change-Id: Ic85fa73c6f3655189438ec509765bae2eab9993a Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
-rw-r--r--src/qml/qml/qqmlproperty.cpp10
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp2
-rw-r--r--src/qml/qml/v8/qv8engine.cpp2
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp21
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp1
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp58
6 files changed, 87 insertions, 7 deletions
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index d1ecfdc52d..cf1b5ffd18 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -1611,10 +1611,14 @@ QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engi
{
if (engine) {
return engine->rawMetaObjectForType(userType);
- } else {
- QQmlType *type = QQmlMetaType::qmlType(userType);
- return QQmlMetaObject(type?type->baseMetaObject():0);
}
+ QQmlType *type = QQmlMetaType::qmlType(userType);
+ if (type)
+ return QQmlMetaObject(type->baseMetaObject());
+ QMetaType metaType(userType);
+ if ((metaType.flags() & QMetaType::PointerToQObject) && metaType.metaObject())
+ return metaType.metaObject();
+ return QQmlMetaObject((QObject*)0);
}
/*!
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 6d44b61aee..a1385e06fc 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -124,7 +124,7 @@ static QQmlPropertyData::Flags flagsForPropertyType(int propType, QQmlEngine *en
engine ? QQmlEnginePrivate::get(engine)->typeCategory(propType)
: QQmlMetaType::typeCategory(propType);
- if (cat == QQmlMetaType::Object)
+ if (cat == QQmlMetaType::Object || QMetaType::typeFlags(propType) & QMetaType::PointerToQObject)
flags |= QQmlPropertyData::IsQObjectDerived;
else if (cat == QQmlMetaType::List)
flags |= QQmlPropertyData::IsQList;
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp
index 117fea272c..6737c14b17 100644
--- a/src/qml/qml/v8/qv8engine.cpp
+++ b/src/qml/qml/v8/qv8engine.cpp
@@ -310,6 +310,8 @@ QV4::Value QV8Engine::fromVariant(const QVariant &variant)
a->arrayData[ii].value = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii));
a->setArrayLengthUnchecked(list.count());
return QV4::Value::fromObject(a);
+ } else if (QMetaType::typeFlags(type) & QMetaType::PointerToQObject) {
+ return QV4::QObjectWrapper::wrap(m_v4Engine, *reinterpret_cast<QObject* const *>(ptr));
}
bool objOk;
diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
index e0c20307a8..4f21231184 100644
--- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
+++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
@@ -72,6 +72,7 @@ private slots:
void qtbug_22535();
void evalAfterInvalidate();
+ void qobjectDerived();
private:
QQmlEngine engine;
@@ -659,6 +660,26 @@ void tst_qqmlcontext::evalAfterInvalidate()
QCoreApplication::processEvents();
}
+void tst_qqmlcontext::qobjectDerived()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("refreshExpressions.qml"));
+
+ CountCommand command;
+ // This test is similar to refreshExpressions, but with the key difference that
+ // we use the QVariant overload of setContextProperty. That way, we test that
+ // QVariant knowledge that it contains a QObject derived pointer is used.
+ engine.rootContext()->setContextProperty("countCommand", QVariant::fromValue(&command));
+
+ // We use a fresh context here to bypass any root-context optimizations in
+ // the engine
+ QQmlContext context(engine.rootContext());
+
+ QObject *o1 = component.create(&context);
+
+ QCOMPARE(command.count, 2);
+}
+
QTEST_MAIN(tst_qqmlcontext)
#include "tst_qqmlcontext.moc"
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 3e2493effe..acb623989a 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -3016,7 +3016,6 @@ void tst_qqmllanguage::signalParameterTypes()
QQmlComponent component(&engine, testFileUrl("signalParameterTypes.2.qml"));
QObject *obj = component.create();
QVERIFY(obj != 0);
- QEXPECT_FAIL("", "Dynamic connections don't enforce type safety - QTBUG-26662", Abort);
QVERIFY(obj->property("success").toBool());
delete obj;
}
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 418e2edf27..27c3fd985e 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -57,11 +57,24 @@ class MyQmlObject : public QObject
{
Q_OBJECT
public:
- MyQmlObject() {}
+ MyQmlObject(QObject *parent = 0) : QObject(parent) {}
};
QML_DECLARE_TYPE(MyQmlObject);
+class MyQObject : public QObject
+{
+ Q_OBJECT
+public:
+ MyQObject(QObject *parent = 0) : QObject(parent), m_i(0) {}
+
+ int inc() { return ++m_i; }
+
+private:
+ int m_i;
+};
+
+
class MyAttached : public QObject
{
Q_OBJECT
@@ -316,10 +329,11 @@ class PropertyObject : public QObject
Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty)
Q_PROPERTY(int propertyWithNotify READ propertyWithNotify WRITE setPropertyWithNotify NOTIFY oddlyNamedNotifySignal)
Q_PROPERTY(MyQmlObject *qmlObject READ qmlObject)
+ Q_PROPERTY(MyQObject *qObject READ qObject WRITE setQObject NOTIFY qObjectChanged)
Q_CLASSINFO("DefaultProperty", "defaultProperty")
public:
- PropertyObject() : m_resetProperty(9) {}
+ PropertyObject() : m_resetProperty(9), m_qObject(0) {}
int defaultProperty() { return 10; }
QRect rectProperty() { return QRect(10, 10, 1, 209); }
@@ -342,9 +356,19 @@ public:
MyQmlObject *qmlObject() { return &m_qmlObject; }
+ MyQObject *qObject() { return m_qObject; }
+ void setQObject(MyQObject *object)
+ {
+ if (m_qObject != object) {
+ m_qObject = object;
+ emit qObjectChanged();
+ }
+ }
+
signals:
void clicked();
void oddlyNamedNotifySignal();
+ void qObjectChanged();
private:
int m_resetProperty;
@@ -353,6 +377,7 @@ private:
QVariantMap m_variantMap;
int m_propertyWithNotify;
MyQmlObject m_qmlObject;
+ MyQObject *m_qObject;
};
QML_DECLARE_TYPE(PropertyObject);
@@ -1130,6 +1155,18 @@ void tst_qqmlproperty::read()
QCOMPARE(p.read(), QVariant());
}
+ // Object property registered with Qt, but not registered with QML.
+ {
+ PropertyObject o;
+ QQmlProperty p(&o, "qObject");
+ QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
+
+ QCOMPARE(p.propertyType(), qMetaTypeId<MyQObject*>());
+ QVariant v = p.read();
+ QVERIFY(v.canConvert(QMetaType::QObjectStar));
+ QVERIFY(qvariant_cast<QObject *>(v) == o.qObject());
+ }
+
// Object property
{
PropertyObject o;
@@ -1387,6 +1424,23 @@ void tst_qqmlproperty::write()
QCOMPARE(p.read(), QVariant(99));
delete object;
}
+ // Writable pointer to QObject derived
+ {
+ PropertyObject o;
+ QQmlProperty p(&o, QString("qObject"));
+ QCOMPARE(o.qObject(), (QObject*)0);
+ QObject *newObject = new MyQObject(this);
+ QCOMPARE(p.write(QVariant::fromValue(newObject)), true);
+ QCOMPARE(o.qObject(), newObject);
+ QVariant data = p.read();
+ QCOMPARE(data.value<QObject*>(), newObject);
+ QCOMPARE(data.value<MyQObject*>(), newObject);
+ // Incompatible types can not be written.
+ QCOMPARE(p.write(QVariant::fromValue(new MyQmlObject(this))), false);
+ QVariant newData = p.read();
+ QCOMPARE(newData.value<QObject*>(), newObject);
+ QCOMPARE(newData.value<MyQObject*>(), newObject);
+ }
}
void tst_qqmlproperty::reset()