diff options
Diffstat (limited to 'src/qml/qml')
-rw-r--r-- | src/qml/qml/qqmlvmemetaobject.cpp | 156 | ||||
-rw-r--r-- | src/qml/qml/qqmlvmemetaobject_p.h | 31 |
2 files changed, 100 insertions, 87 deletions
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 0cef04ab89..a86c249e40 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -272,9 +272,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache) : object(obj), - cache(cache), - interceptors(nullptr), - metaObject(nullptr) + cache(cache) { QObjectPrivate *op = QObjectPrivate::get(obj); @@ -312,90 +310,86 @@ int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, return object->qt_metacall(c, id, a); } -bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) +bool QQmlInterceptorMetaObject::doIntercept(QMetaObject::Call c, int id, void **a) { - if ( ( (c == QMetaObject::WriteProperty && - !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyData::BypassInterceptor)) || c == QMetaObject::BindableProperty ) - && interceptors ) { - - for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { - if (vi->m_propertyIndex.coreIndex() != id) - continue; - - const int valueIndex = vi->m_propertyIndex.valueTypeIndex(); - const QQmlData *data = QQmlData::get(object); - const QMetaType metaType = data->propertyCache->property(id)->propType(); - - if (metaType.isValid()) { - if (valueIndex != -1 && c == QMetaObject::WriteProperty) { - // TODO: handle intercepting bindable properties for value types? - QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( - data->context->engine(), metaType); - Q_ASSERT(valueType); - - // - // Consider the following case: - // color c = { 0.1, 0.2, 0.3 } - // interceptor exists on c.r - // write { 0.2, 0.4, 0.6 } - // - // The interceptor may choose not to update the r component at this - // point (for example, a behavior that creates an animation). But we - // need to ensure that the g and b components are updated correctly. - // - // So we need to perform a full write where the value type is: - // r = old value, g = new value, b = new value - // - // And then call the interceptor which may or may not write the - // new value to the r component. - // - // This will ensure that the other components don't contain stale data - // and any relevant signals are emitted. - // - // To achieve this: - // (1) Store the new value type as a whole (needed due to - // aliasing between a[0] and static storage in value type). - // (2) Read the entire existing value type from object -> valueType temp. - // (3) Read the previous value of the component being changed - // from the valueType temp. - // (4) Write the entire new value type into the temp. - // (5) Overwrite the component being changed with the old value. - // (6) Perform a full write to the value type (which may emit signals etc). - // (7) Issue the interceptor call with the new component value. - // - - QMetaProperty valueProp = valueType->property(valueIndex); - QVariant newValue(metaType, a[0]); - - valueType->read(object, id); - QVariant prevComponentValue = valueProp.read(valueType); - - valueType->setValue(newValue); - QVariant newComponentValue = valueProp.read(valueType); - - // Don't apply the interceptor if the intercepted value has not changed - bool updated = false; - if (newComponentValue != prevComponentValue) { - valueProp.write(valueType, prevComponentValue); - valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); - - vi->write(newComponentValue); - updated = true; - } + for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { + if (vi->m_propertyIndex.coreIndex() != id) + continue; + + const int valueIndex = vi->m_propertyIndex.valueTypeIndex(); + const QQmlData *data = QQmlData::get(object); + const QMetaType metaType = data->propertyCache->property(id)->propType(); + + if (metaType.isValid()) { + if (valueIndex != -1 && c == QMetaObject::WriteProperty) { + // TODO: handle intercepting bindable properties for value types? + QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( + data->context->engine(), metaType); + Q_ASSERT(valueType); + + // + // Consider the following case: + // color c = { 0.1, 0.2, 0.3 } + // interceptor exists on c.r + // write { 0.2, 0.4, 0.6 } + // + // The interceptor may choose not to update the r component at this + // point (for example, a behavior that creates an animation). But we + // need to ensure that the g and b components are updated correctly. + // + // So we need to perform a full write where the value type is: + // r = old value, g = new value, b = new value + // + // And then call the interceptor which may or may not write the + // new value to the r component. + // + // This will ensure that the other components don't contain stale data + // and any relevant signals are emitted. + // + // To achieve this: + // (1) Store the new value type as a whole (needed due to + // aliasing between a[0] and static storage in value type). + // (2) Read the entire existing value type from object -> valueType temp. + // (3) Read the previous value of the component being changed + // from the valueType temp. + // (4) Write the entire new value type into the temp. + // (5) Overwrite the component being changed with the old value. + // (6) Perform a full write to the value type (which may emit signals etc). + // (7) Issue the interceptor call with the new component value. + // + + QMetaProperty valueProp = valueType->property(valueIndex); + QVariant newValue(metaType, a[0]); + + valueType->read(object, id); + QVariant prevComponentValue = valueProp.read(valueType); + + valueType->setValue(newValue); + QVariant newComponentValue = valueProp.read(valueType); + + // Don't apply the interceptor if the intercepted value has not changed + bool updated = false; + if (newComponentValue != prevComponentValue) { + valueProp.write(valueType, prevComponentValue); + valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); + + vi->write(newComponentValue); + updated = true; + } - if (updated) - return true; - } else if (c == QMetaObject::WriteProperty) { - vi->write(QVariant(metaType, a[0])); + if (updated) return true; - } else { - object->qt_metacall(c, id, a); - QUntypedBindable target = *reinterpret_cast<QUntypedBindable *>(a[0]); - return vi->bindable(reinterpret_cast<QUntypedBindable *>(a[0]), target); - } + } else if (c == QMetaObject::WriteProperty) { + vi->write(QVariant(metaType, a[0])); + return true; + } else { + object->qt_metacall(c, id, a); + QUntypedBindable target = *reinterpret_cast<QUntypedBindable *>(a[0]); + return vi->bindable(reinterpret_cast<QUntypedBindable *>(a[0]), target); } } } + return false; } diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 0a5b8c6785..19aba84258 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -115,17 +115,36 @@ public: return false; } + QObject *object = nullptr; + QQmlRefPointer<QQmlPropertyCache> cache; + protected: int metaCall(QObject *o, QMetaObject::Call c, int id, void **a) override; - bool intercept(QMetaObject::Call c, int id, void **a); + bool intercept(QMetaObject::Call c, int id, void **a) + { + if (!interceptors) + return false; + + switch (c) { + case QMetaObject::WriteProperty: + if (*reinterpret_cast<int*>(a[3]) & QQmlPropertyData::BypassInterceptor) + return false; + break; + case QMetaObject::BindableProperty: + break; + default: + return false; + } + + return doIntercept(c, id, a); + } -public: - QObject *object; - QQmlRefPointer<QQmlPropertyCache> cache; QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent; + const QMetaObject *metaObject = nullptr; - QQmlPropertyValueInterceptor *interceptors; - const QMetaObject *metaObject; +private: + bool doIntercept(QMetaObject::Call c, int id, void **a); + QQmlPropertyValueInterceptor *interceptors = nullptr; }; inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj) |