Support for QQmlVMEMetaObject and QmlPropertyCache

We have to derive NodeInstanceMetaObject from QQmlVMEMetaObject
to support e. g. alias properties.
The QmlEngine uses QQmlVMEMetaObject to resolve
alias properties.
Also we have to invalidate/recreate the
QmlPropertyCache if we add dynamic properties.

Change-Id: Id52dd8b380eadeb6f74b5171168f2f70c16be118
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
This commit is contained in:
Thomas Hartmann
2012-10-23 14:39:18 +02:00
parent 38f9169ef5
commit 2a624649af
3 changed files with 301 additions and 37 deletions

View File

@@ -36,23 +36,167 @@
#include <qnumeric.h> #include <qnumeric.h>
#include <QDebug> #include <QDebug>
#include <private/qqmlengine_p.h>
#include <private/qqmlpropertycache_p.h>
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {
NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstance::Pointer &nodeInstance, QQmlEngine *engine) static QHash<QDynamicMetaObjectData *, bool> nodeInstanceMetaObjectList;
: QQmlOpenMetaObject(nodeInstance->object(), new QQmlOpenMetaObjectType(nodeInstance->object()->metaObject(), engine), true),
m_nodeInstance(nodeInstance), struct MetaPropertyData {
m_context(nodeInstance->isRootNodeInstance() ? nodeInstance->context() : 0) inline QPair<QVariant, bool> &getDataRef(int idx) {
while (m_data.count() <= idx)
m_data << QPair<QVariant, bool>(QVariant(), false);
return m_data[idx];
}
inline QVariant &getData(int idx) {
QPair<QVariant, bool> &prop = getDataRef(idx);
if (!prop.second) {
prop.first = QVariant();
prop.second = true;
}
return prop.first;
}
inline bool hasData(int idx) const {
if (idx >= m_data.count())
return false;
return m_data[idx].second;
}
inline int count() { return m_data.count(); }
QList<QPair<QVariant, bool> > m_data;
};
static bool constructedMetaData(const QQmlVMEMetaData* data)
{ {
setCached(false); return data->varPropertyCount == 0
&& data->propertyCount == 0
&& data->aliasCount == 0
&& data->signalCount == 0
&& data->methodCount == 0;
}
static QQmlVMEMetaData* fakeMetaData()
{
QQmlVMEMetaData* data = new QQmlVMEMetaData;
data->varPropertyCount = 0;
data->propertyCount = 0;
data->aliasCount = 0;
data->signalCount = 0;
data->methodCount = 0;
return data;
}
static const QQmlVMEMetaData* vMEMetaDataForObject(QObject *object)
{
QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
if (metaObject)
return metaObject->metaData;
return fakeMetaData();
}
static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine)
{
QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
if (metaObject)
return metaObject->cache;
return QQmlEnginePrivate::get(engine)->cache(object);
}
static QAbstractDynamicMetaObject *abstractDynamicMetaObject(QObject *object)
{
QObjectPrivate *op = QObjectPrivate::get(object);
if (op->metaObject)
return static_cast<QAbstractDynamicMetaObject *>(op->metaObject);
return const_cast<QAbstractDynamicMetaObject *>(static_cast<const QAbstractDynamicMetaObject *>(object->metaObject()));
}
NodeInstanceMetaObject *NodeInstanceMetaObject::createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine)
{
//Avoid setting up multiple NodeInstanceMetaObjects on the same QObject
QObjectPrivate *op = QObjectPrivate::get(nodeInstance->object());
QDynamicMetaObjectData *parent = op->metaObject;
if (nodeInstanceMetaObjectList.contains(parent))
return static_cast<NodeInstanceMetaObject *>(parent);
return new NodeInstanceMetaObject(nodeInstance, engine);
}
NodeInstanceMetaObject *NodeInstanceMetaObject::createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine)
{
//Avoid setting up multiple NodeInstanceMetaObjects on the same QObject
QObjectPrivate *op = QObjectPrivate::get(nodeInstance->object());
QDynamicMetaObjectData *parent = op->metaObject;
if (nodeInstanceMetaObjectList.contains(parent))
return static_cast<NodeInstanceMetaObject *>(parent);
return new NodeInstanceMetaObject(nodeInstance, object, prefix, engine);
}
void NodeInstanceMetaObject::init(QObject *object, QQmlEngine *engine)
{
//Creating QQmlOpenMetaObjectType
m_type = new QQmlOpenMetaObjectType(metaObjectParent(), engine);
m_type->addref();
//Assigning type to this
copyTypeMetaObject();
//Assign this to object
QObjectPrivate *op = QObjectPrivate::get(object);
op->metaObject = this;
//create cache
cache = m_cache = QQmlEnginePrivate::get(engine)->cache(this);
//If our parent is not a VMEMetaObject we just se the flag to false again
if (constructedMetaData(metaData))
QQmlData::get(object)->hasVMEMetaObject = false;
nodeInstanceMetaObjectList.insert(this, true);
}
NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstance::Pointer &nodeInstance, QQmlEngine *engine)
: QQmlVMEMetaObject(nodeInstance->object(), cacheForObject(nodeInstance->object(), engine), vMEMetaDataForObject(nodeInstance->object())),
m_nodeInstance(nodeInstance),
m_context(engine->contextForObject(nodeInstance->object())),
m_data(new MetaPropertyData),
m_cache(0)
{
init(nodeInstance->object(), engine);
QQmlData *ddata = QQmlData::get(nodeInstance->object(), false);
//Assign cache to object
if (ddata && ddata->propertyCache) {
ddata->propertyCache = m_cache;
}
} }
NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine) NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine)
: QQmlOpenMetaObject(object, new QQmlOpenMetaObjectType(object->metaObject(), engine), true), : QQmlVMEMetaObject(object, cacheForObject(object, engine), vMEMetaDataForObject(object)),
m_nodeInstance(nodeInstance), m_nodeInstance(nodeInstance),
m_prefix(prefix) m_context(engine->contextForObject(object)),
m_prefix(prefix),
m_data(new MetaPropertyData),
m_cache(0)
{ {
setCached(false); init(object, engine);
}
NodeInstanceMetaObject::~NodeInstanceMetaObject()
{
m_type->release();
nodeInstanceMetaObjectList.remove(this);
} }
void NodeInstanceMetaObject::createNewProperty(const QString &name) void NodeInstanceMetaObject::createNewProperty(const QString &name)
@@ -60,55 +204,114 @@ void NodeInstanceMetaObject::createNewProperty(const QString &name)
int id = createProperty(name.toLatin1(), 0); int id = createProperty(name.toLatin1(), 0);
setValue(id, QVariant()); setValue(id, QVariant());
Q_ASSERT(id >= 0); Q_ASSERT(id >= 0);
Q_UNUSED(id) Q_UNUSED(id);
//Updating cache
QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this);
QQmlProperty property(myObject(), name, m_context);
Q_ASSERT(property.isValid());
}
int NodeInstanceMetaObject::createProperty(const char *name, const char *)
{
int id = m_type->createProperty(name);
copyTypeMetaObject();
return id;
}
void NodeInstanceMetaObject::setValue(int id, const QVariant &value)
{
QPair<QVariant, bool> &prop = m_data->getDataRef(id);
prop.first = propertyWriteValue(id, value);
prop.second = true;
QMetaObject::activate(myObject(), id + m_type->signalOffset(), 0);
}
QVariant NodeInstanceMetaObject::propertyWriteValue(int, const QVariant &value)
{
return value;
}
int NodeInstanceMetaObject::openMetaCall(QMetaObject::Call call, int id, void **a)
{
if ((call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty)
&& id >= m_type->propertyOffset()) {
int propId = id - m_type->propertyOffset();
if (call == QMetaObject::ReadProperty) {
//propertyRead(propId);
*reinterpret_cast<QVariant *>(a[0]) = m_data->getData(propId);
} else if (call == QMetaObject::WriteProperty) {
if (propId <= m_data->count() || m_data->m_data[propId].first != *reinterpret_cast<QVariant *>(a[0])) {
//propertyWrite(propId);
QPair<QVariant, bool> &prop = m_data->getDataRef(propId);
prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]));
prop.second = true;
//propertyWritten(propId);
activate(myObject(), m_type->signalOffset() + propId, 0);
}
}
return -1;
} else {
QAbstractDynamicMetaObject *directParent = parent();
if (directParent)
return directParent->metaCall(call, id, a);
else
return myObject()->qt_metacall(call, id, a);
}
} }
int NodeInstanceMetaObject::metaCall(QMetaObject::Call call, int id, void **a) int NodeInstanceMetaObject::metaCall(QMetaObject::Call call, int id, void **a)
{ {
int metaCallReturnValue = -1; int metaCallReturnValue = -1;
const QMetaProperty propertyById = QQmlVMEMetaObject::property(id);
if (call == QMetaObject::WriteProperty if (call == QMetaObject::WriteProperty
&& property(id).userType() == QMetaType::QVariant && propertyById.userType() == QMetaType::QVariant
&& reinterpret_cast<QVariant *>(a[0])->type() == QVariant::Double && reinterpret_cast<QVariant *>(a[0])->type() == QVariant::Double
&& qIsNaN(reinterpret_cast<QVariant *>(a[0])->toDouble())) { && qIsNaN(reinterpret_cast<QVariant *>(a[0])->toDouble())) {
return -1; return -1;
} }
if (call == QMetaObject::WriteProperty if (call == QMetaObject::WriteProperty
&& property(id).userType() == QMetaType::Double && propertyById.userType() == QMetaType::Double
&& qIsNaN(*reinterpret_cast<double*>(a[0]))) { && qIsNaN(*reinterpret_cast<double*>(a[0]))) {
return -1; return -1;
} }
if (call == QMetaObject::WriteProperty if (call == QMetaObject::WriteProperty
&& property(id).userType() == QMetaType::Float && propertyById.userType() == QMetaType::Float
&& qIsNaN(*reinterpret_cast<float*>(a[0]))) { && qIsNaN(*reinterpret_cast<float*>(a[0]))) {
return -1; return -1;
} }
QVariant oldValue; QVariant oldValue;
if (call == QMetaObject::WriteProperty && !property(id).hasNotifySignal()) if (call == QMetaObject::WriteProperty && !propertyById.hasNotifySignal())
{ {
oldValue = property(id).read(object()); oldValue = propertyById.read(myObject());
} }
ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef(); ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef();
if (parent() && id < parent()->propertyOffset()) { QAbstractDynamicMetaObject *directParent = parent();
metaCallReturnValue = parent()->metaCall(call, id, a); if (directParent && id < directParent->propertyOffset()) {
metaCallReturnValue = directParent->metaCall(call, id, a);
} else { } else {
metaCallReturnValue = QQmlOpenMetaObject::metaCall(call, id, a); openMetaCall(call, id, a);
} }
if ((call == QMetaObject::WriteProperty || call == QMetaObject::ReadProperty) && metaCallReturnValue < 0) { if ((call == QMetaObject::WriteProperty || call == QMetaObject::ReadProperty) && metaCallReturnValue < 0) {
if (objectNodeInstance if (objectNodeInstance
&& objectNodeInstance->nodeInstanceServer() && objectNodeInstance->nodeInstanceServer()
&& objectNodeInstance->nodeInstanceServer()->dummyContextObject() && objectNodeInstance->nodeInstanceServer()->dummyContextObject()
&& !(objectNodeInstance && !objectNodeInstance->isRootNodeInstance() && property(id).name() == QLatin1String("parent"))) { && !(objectNodeInstance && !objectNodeInstance->isRootNodeInstance()
&& property(id).name() == QLatin1String("parent"))) {
QObject *contextDummyObject = objectNodeInstance->nodeInstanceServer()->dummyContextObject(); QObject *contextDummyObject = objectNodeInstance->nodeInstanceServer()->dummyContextObject();
int properyIndex = contextDummyObject->metaObject()->indexOfProperty(property(id).name()); int properyIndex = contextDummyObject->metaObject()->indexOfProperty(propertyById.name());
if (properyIndex >= 0) if (properyIndex >= 0)
metaCallReturnValue = contextDummyObject->qt_metacall(call, properyIndex, a); metaCallReturnValue = contextDummyObject->qt_metacall(call, properyIndex, a);
} }
@@ -116,8 +319,8 @@ int NodeInstanceMetaObject::metaCall(QMetaObject::Call call, int id, void **a)
if (metaCallReturnValue >= 0 if (metaCallReturnValue >= 0
&& call == QMetaObject::WriteProperty && call == QMetaObject::WriteProperty
&& !property(id).hasNotifySignal() && !propertyById.hasNotifySignal()
&& oldValue != property(id).read(object())) && oldValue != propertyById.read(myObject()))
notifyPropertyChange(id); notifyPropertyChange(id);
return metaCallReturnValue; return metaCallReturnValue;
@@ -126,15 +329,31 @@ int NodeInstanceMetaObject::metaCall(QMetaObject::Call call, int id, void **a)
void NodeInstanceMetaObject::notifyPropertyChange(int id) void NodeInstanceMetaObject::notifyPropertyChange(int id)
{ {
ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef(); ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef();
const QMetaProperty propertyById = property(id);
if (objectNodeInstance && objectNodeInstance->nodeInstanceServer()) { if (objectNodeInstance && objectNodeInstance->nodeInstanceServer()) {
if (id < type()->propertyOffset()) { if (id < propertyOffset()) {
objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + property(id).name()); objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + propertyById.name());
} else { } else {
objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + name(id - type()->propertyOffset())); objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + name(id - propertyOffset()));
} }
} }
} }
int NodeInstanceMetaObject::count() const
{
return m_type->propertyCount();
}
QByteArray NodeInstanceMetaObject::name(int idx) const
{
return m_type->propertyName(idx);
}
void NodeInstanceMetaObject::copyTypeMetaObject()
{
*static_cast<QMetaObject *>(this) = *m_type->metaObject();
}
} // namespace Internal } // namespace Internal
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -32,7 +32,9 @@
#define NODEINSTANCEMETAOBJECT_H #define NODEINSTANCEMETAOBJECT_H
#include <QQmlContext> #include <QQmlContext>
#include <QScopedPointer>
#include <private/qqmlopenmetaobject_p.h> #include <private/qqmlopenmetaobject_p.h>
#include <private/qqmlvmemetaobject_p.h>
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {
@@ -41,21 +43,63 @@ class ObjectNodeInstance;
typedef QSharedPointer<ObjectNodeInstance> ObjectNodeInstancePointer; typedef QSharedPointer<ObjectNodeInstance> ObjectNodeInstancePointer;
typedef QWeakPointer<ObjectNodeInstance> ObjectNodeInstanceWeakPointer; typedef QWeakPointer<ObjectNodeInstance> ObjectNodeInstanceWeakPointer;
class NodeInstanceMetaObject : public QQmlOpenMetaObject struct MetaPropertyData;
class NodeInstanceMetaObject : public QQmlVMEMetaObject
{ {
public: public:
NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine); static NodeInstanceMetaObject *createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine);
NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine); static NodeInstanceMetaObject *createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine);
~NodeInstanceMetaObject();
void createNewProperty(const QString &name); void createNewProperty(const QString &name);
protected: protected:
NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine);
NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine);
int openMetaCall(QMetaObject::Call _c, int _id, void **_a);
int metaCall(QMetaObject::Call _c, int _id, void **_a); int metaCall(QMetaObject::Call _c, int _id, void **_a);
void notifyPropertyChange(int id); void notifyPropertyChange(int id);
void setValue(int id, const QVariant &value);
int createProperty(const char *, const char *);
QVariant propertyWriteValue(int, const QVariant &);
QObject *myObject() const { return QQmlVMEMetaObject::object; }
QAbstractDynamicMetaObject *parent() const { return const_cast<QAbstractDynamicMetaObject *>(dynamicMetaObjectParent()); }
const QAbstractDynamicMetaObject *dynamicMetaObjectParent() const
{
if (QQmlVMEMetaObject::parent.isT1())
return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object);
else
return 0;
}
const QMetaObject *metaObjectParent() const
{
if (QQmlVMEMetaObject::parent.isT1())
return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object);
return QQmlVMEMetaObject::parent.asT2();
}
int propertyOffset() const { return cache->propertyOffset(); }
int count() const;
QByteArray name(int) const;
void copyTypeMetaObject();
private: private:
void init(QObject *, QQmlEngine *engine);
ObjectNodeInstanceWeakPointer m_nodeInstance; ObjectNodeInstanceWeakPointer m_nodeInstance;
QString m_prefix; QString m_prefix;
QPointer<QQmlContext> m_context; QPointer<QQmlContext> m_context;
QQmlOpenMetaObjectType *m_type;
QScopedPointer<MetaPropertyData> m_data;
//QAbstractDynamicMetaObject *m_parent;
QQmlPropertyCache *m_cache;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -130,14 +130,15 @@ static bool hasPropertiesWitoutNotifications(const QMetaObject *metaObject)
void ObjectNodeInstance::initializePropertyWatcher(const ObjectNodeInstance::Pointer &objectNodeInstance) void ObjectNodeInstance::initializePropertyWatcher(const ObjectNodeInstance::Pointer &objectNodeInstance)
{ {
const QMetaObject *metaObject = objectNodeInstance->object()->metaObject(); const QMetaObject *metaObject = objectNodeInstance->object()->metaObject();
m_metaObject = new NodeInstanceMetaObject(objectNodeInstance, nodeInstanceServer()->engine()); m_metaObject = NodeInstanceMetaObject::createNodeInstanceMetaObject(objectNodeInstance, nodeInstanceServer()->engine());
QQmlEnginePrivate::get(engine())->cache(m_metaObject);
for (int propertyIndex = QObject::staticMetaObject.propertyCount(); propertyIndex < metaObject->propertyCount(); propertyIndex++) { for (int propertyIndex = QObject::staticMetaObject.propertyCount(); propertyIndex < metaObject->propertyCount(); propertyIndex++) {
if (QQmlMetaType::isQObject(metaObject->property(propertyIndex).userType())) { if (QQmlMetaType::isQObject(metaObject->property(propertyIndex).userType())) {
QObject *propertyObject = QQmlMetaType::toQObject(metaObject->property(propertyIndex).read(objectNodeInstance->object())); QObject *propertyObject = QQmlMetaType::toQObject(metaObject->property(propertyIndex).read(objectNodeInstance->object()));
if (propertyObject && hasPropertiesWitoutNotifications(propertyObject->metaObject())) { if (propertyObject && hasPropertiesWitoutNotifications(propertyObject->metaObject())) {
QMetaObject *childMetaObject = new NodeInstanceMetaObject(objectNodeInstance, propertyObject, metaObject->property(propertyIndex).name(), nodeInstanceServer()->engine()); NodeInstanceMetaObject::createNodeInstanceMetaObject(objectNodeInstance,
QQmlEnginePrivate::get(engine())->cache(childMetaObject); propertyObject,
metaObject->property(propertyIndex).name(),
nodeInstanceServer()->engine());
} }
} }
} }