Allow access to signals and slots in QQmlPropertyMap inheritors
authorMatthew Vogt <matthew.vogt@nokia.com>
Thu, 26 Jul 2012 04:15:49 +0000 (14:15 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 27 Jul 2012 04:09:11 +0000 (06:09 +0200)
Allow inheritors of QQmlPropertyMap to pass the static meta object
information needed to support their signals and slots.  If the correct
pointer is not provided to the constructor, it is not accessible via
the virtual metaObject() function during construction.

Task-number: QTBUG-26400
Change-Id: Ide8c6d3568e4abd4c48e0aa60e6fa05a9c2a11cf
Reviewed-by: Martin Jones <martin.jones@nokia.com>

src/qml/qml/qqmlopenmetaobject.cpp
src/qml/qml/qqmlopenmetaobject_p.h
src/qml/util/qqmlpropertymap.cpp
src/qml/util/qqmlpropertymap.h
tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp

index 4774f7e..bd40cc3 100644 (file)
@@ -175,13 +175,13 @@ public:
     bool cacheProperties;
 };
 
-QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, bool automatic)
+QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bool automatic)
 : d(new QQmlOpenMetaObjectPrivate(this))
 {
     d->autoCreate = automatic;
     d->object = obj;
 
-    d->type = new QQmlOpenMetaObjectType(obj->metaObject(), 0);
+    d->type = new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), 0);
     d->type->d->referers.insert(this);
 
     QObjectPrivate *op = QObjectPrivate::get(obj);
index a45d7d8..bfd96f7 100644 (file)
@@ -83,7 +83,7 @@ class QQmlOpenMetaObjectPrivate;
 class Q_QML_PRIVATE_EXPORT QQmlOpenMetaObject : public QAbstractDynamicMetaObject
 {
 public:
-    QQmlOpenMetaObject(QObject *, bool = true);
+    QQmlOpenMetaObject(QObject *, const QMetaObject * = 0, bool = true);
     QQmlOpenMetaObject(QObject *, QQmlOpenMetaObjectType *, bool = true);
     ~QQmlOpenMetaObject();
 
index cdc82fe..37f9e31 100644 (file)
@@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE
 class QQmlPropertyMapMetaObject : public QQmlOpenMetaObject
 {
 public:
-    QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv);
+    QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv, const QMetaObject *staticMetaObject);
 
 protected:
     virtual QVariant propertyWriteValue(int, const QVariant &);
@@ -110,7 +110,8 @@ const QString &QQmlPropertyMapPrivate::propertyName(int index) const
     return keys[index];
 }
 
-QQmlPropertyMapMetaObject::QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv) : QQmlOpenMetaObject(obj)
+QQmlPropertyMapMetaObject::QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv, const QMetaObject *staticMetaObject)
+    : QQmlOpenMetaObject(obj, staticMetaObject)
 {
     map = obj;
     priv = objPriv;
@@ -176,16 +177,20 @@ int QQmlPropertyMapMetaObject::createProperty(const char *name, const char *valu
 
     \note It is not possible to remove keys from the map; once a key has been added, you can only
     modify or clear its associated value.
+
+    \note When deriving a class from QQmlPropertyMap, use the
+    \l {QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)}
+    {protected two-argument constructor}
+    which ensures that the class is correctly registered with the Qt \l {Meta-Object System}.
 */
 
 /*!
     Constructs a bindable map with parent object \a parent.
 */
 QQmlPropertyMap::QQmlPropertyMap(QObject *parent)
-: QObject(*(new QQmlPropertyMapPrivate), parent)
+: QObject(*allocatePrivate(), parent)
 {
-    Q_D(QQmlPropertyMap);
-    d->mo = new QQmlPropertyMapMetaObject(this, d);
+    init(metaObject());
 }
 
 /*!
@@ -331,9 +336,23 @@ QVariant QQmlPropertyMap::operator[](const QString &key) const
 */
 QVariant QQmlPropertyMap::updateValue(const QString &key, const QVariant &input)
 {
+    Q_UNUSED(key)
     return input;
 }
 
+/*! \internal */
+void QQmlPropertyMap::init(const QMetaObject *staticMetaObject)
+{
+    Q_D(QQmlPropertyMap);
+    d->mo = new QQmlPropertyMapMetaObject(this, d, staticMetaObject);
+}
+
+/*! \internal */
+QObjectPrivate *QQmlPropertyMap::allocatePrivate()
+{
+    return new QQmlPropertyMapPrivate;
+}
+
 /*!
     \fn void QQmlPropertyMap::valueChanged(const QString &key, const QVariant &value)
     This signal is emitted whenever one of the values in the map is changed. \a key
@@ -343,4 +362,15 @@ QVariant QQmlPropertyMap::updateValue(const QString &key, const QVariant &input)
     or clear() - it is only emitted when a value is updated from QML.
 */
 
+/*!
+    \fn QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)
+
+    Constructs a bindable map with parent object \a parent.  Use this constructor
+    in classes derived from QQmlPropertyMap.
+
+    The type of \a derived is used to register the property map with the \l {Meta-Object System},
+    which is necessary to ensure that properties of the derived class are accessible.
+    This type must be derived from QQmlPropertyMap.
+*/
+
 QT_END_NAMESPACE
index bfd11e5..f7d69be 100644 (file)
@@ -82,7 +82,18 @@ Q_SIGNALS:
 protected:
     virtual QVariant updateValue(const QString &key, const QVariant &input);
 
+    template<class DerivedType>
+    QQmlPropertyMap(DerivedType *derived, QObject *parent = 0)
+        : QObject(*allocatePrivate(), parent)
+    {
+        Q_UNUSED(derived)
+        init(&DerivedType::staticMetaObject);
+    }
+
 private:
+    void init(const QMetaObject *staticMetaObject);
+    static QObjectPrivate *allocatePrivate();
+
     Q_DECLARE_PRIVATE(QQmlPropertyMap)
     Q_DISABLE_COPY(QQmlPropertyMap)
 };
index 4e39ad3..3fd9dbe 100644 (file)
@@ -64,6 +64,7 @@ private slots:
 
     void crashBug();
     void QTBUG_17868();
+    void metaObjectAccessibility();
 };
 
 void tst_QQmlPropertyMap::insert()
@@ -280,6 +281,48 @@ void tst_QQmlPropertyMap::QTBUG_17868()
 
 }
 
+class MyEnhancedPropertyMap : public QQmlPropertyMap
+{
+    Q_OBJECT
+public:
+    MyEnhancedPropertyMap() : QQmlPropertyMap(this) {}
+
+signals:
+    void testSignal();
+
+public slots:
+    void testSlot() {}
+};
+
+namespace
+{
+    QStringList messages;
+    void msgHandler(QtMsgType, const char *msg)
+    {
+        messages << QLatin1String(msg);
+    }
+}
+
+void tst_QQmlPropertyMap::metaObjectAccessibility()
+{
+    messages.clear();
+    QtMsgHandler old = qInstallMsgHandler(msgHandler);
+
+    QQmlEngine engine;
+
+    MyEnhancedPropertyMap map;
+
+    // Verify that signals and slots of QQmlPropertyMap-derived classes are accessible
+    QObject::connect(&map, SIGNAL(testSignal()), &engine, SIGNAL(quit()));
+    QObject::connect(&engine, SIGNAL(quit()), &map, SLOT(testSlot()));
+
+    QCOMPARE(map.metaObject()->className(), "MyEnhancedPropertyMap");
+
+    qInstallMsgHandler(old);
+
+    QCOMPARE(messages.count(), 0);
+}
+
 QTEST_MAIN(tst_QQmlPropertyMap)
 
 #include "tst_qqmlpropertymap.moc"