aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/qml/qqmldata_p.h3
-rw-r--r--src/qml/qml/qqmlengine.cpp2
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp17
-rw-r--r--src/qml/qml/qqmlpropertyvalueinterceptor_p.h2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp226
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h56
-rw-r--r--tests/auto/quick/qquickbehaviors/data/Accelerator.qml18
-rw-r--r--tests/auto/quick/qquickbehaviors/data/aliased.qml39
-rw-r--r--tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp18
9 files changed, 267 insertions, 114 deletions
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 3d2a76693a..ef05dd1fd7 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -112,9 +112,10 @@ public:
* v8 GC will check this flag, only deletes the objects when rootObjectInCreation is false.
*/
quint32 rootObjectInCreation:1;
+ quint32 hasInterceptorMetaObject:1;
quint32 hasVMEMetaObject:1;
quint32 parentFrozen:1;
- quint32 dummy:22;
+ quint32 dummy:21;
// When bindingBitsSize < 32, we store the binding bit flags inside
// bindingBitsValue. When we need more than 32 bits, we allocated
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 93e66a1b12..e8422474d3 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -667,7 +667,7 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
QQmlData::QQmlData()
: ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false),
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
- hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0),
+ hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0),
bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0),
lineNumber(0), columnNumber(0), jsEngineId(0), compiledData(0), deferredData(0),
propertyCache(0), guards(0), extendedData(0)
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index c03a463c83..21e6d5f6de 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -859,11 +859,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast);
QObject *target = createdSubObject->parent();
+ if (targetCorePropertyData.isAlias()) {
+ int propIndex;
+ QQmlPropertyPrivate::findAliasTarget(target, targetCorePropertyData.coreIndex, &target, &propIndex);
+ QQmlData *data = QQmlData::get(target);
+ if (!data || !data->propertyCache) {
+ qWarning() << "can't resolve property alias for 'on' assignment";
+ return false;
+ }
+ targetCorePropertyData = *data->propertyCache->property(propIndex);
+ }
+
QQmlProperty prop =
QQmlPropertyPrivate::restore(target, targetCorePropertyData, context);
+
vi->setTarget(prop);
- QQmlVMEMetaObject *mo = QQmlVMEMetaObject::get(target);
- Q_ASSERT(mo);
+ QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(target);
+ if (!mo)
+ mo = new QQmlInterceptorMetaObject(target, QQmlData::get(target)->propertyCache);
mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi);
return true;
}
diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
index ea267f9c30..6403e85f2a 100644
--- a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
+++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
@@ -60,7 +60,7 @@ public:
virtual void write(const QVariant &value) = 0;
private:
- friend class QQmlVMEMetaObject;
+ friend class QQmlInterceptorMetaObject;
int m_coreIndex;
int m_valueTypeCoreIndex;
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 67c4295d95..37dbfec2a3 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -130,7 +130,131 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
}
}
-QAbstractDynamicMetaObject *QQmlVMEMetaObject::toDynamicMetaObject(QObject *o)
+
+QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache)
+ : object(obj),
+ cache(cache),
+ interceptors(0),
+ hasAssignedMetaObjectData(false)
+{
+ QObjectPrivate *op = QObjectPrivate::get(obj);
+
+ if (op->metaObject) {
+ parent = op->metaObject;
+ // Use the extra flag in QBiPointer to know if we can safely cast parent.asT1() to QQmlVMEMetaObject*
+ parent.setFlagValue(QQmlData::get(obj)->hasVMEMetaObject);
+ } else {
+ parent = obj->metaObject();
+ }
+
+ op->metaObject = this;
+ QQmlData::get(obj)->hasInterceptorMetaObject = true;
+}
+
+QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject()
+{
+
+}
+
+void QQmlInterceptorMetaObject::registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor)
+{
+ interceptor->m_coreIndex = index;
+ interceptor->m_valueTypeCoreIndex = valueIndex;
+ interceptor->m_next = interceptors;
+ interceptors = interceptor;
+}
+
+int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a)
+{
+ Q_ASSERT(o == object);
+ Q_UNUSED(o);
+
+ if (intercept(c, id, a))
+ return -1;
+ return object->qt_metacall(c, id, a);
+}
+
+bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a)
+{
+ if (c == QMetaObject::WriteProperty && interceptors &&
+ !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) {
+
+ for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
+ if (vi->m_coreIndex != id)
+ continue;
+
+ int valueIndex = vi->m_valueTypeCoreIndex;
+ int type = QQmlData::get(object)->propertyCache->property(id)->propType;
+
+ if (type != QVariant::Invalid) {
+ if (valueIndex != -1) {
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
+ 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->metaObject()->property(valueIndex);
+ QVariant newValue(type, 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, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+
+ vi->write(newComponentValue);
+ updated = true;
+ }
+
+ if (updated)
+ return true;
+ } else {
+ vi->write(QVariant(type, a[0]));
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObject *o)
{
if (!hasAssignedMetaObjectData) {
*static_cast<QMetaObject *>(this) = *cache->createMetaObject();
@@ -149,23 +273,13 @@ QAbstractDynamicMetaObject *QQmlVMEMetaObject::toDynamicMetaObject(QObject *o)
QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj,
QQmlPropertyCache *cache,
const QQmlVMEMetaData *meta)
- : object(obj),
- ctxt(QQmlData::get(obj, true)->outerContext), cache(cache), metaData(meta),
- hasAssignedMetaObjectData(false), aliasEndpoints(0),
- interceptors(0), methods(0)
+ : QQmlInterceptorMetaObject(obj, cache),
+ ctxt(QQmlData::get(obj, true)->outerContext), metaData(meta),
+ aliasEndpoints(0),
+ methods(0)
{
cache->addref();
- QObjectPrivate *op = QObjectPrivate::get(obj);
-
- if (op->metaObject) {
- parent = op->metaObject;
- // Use the extra flag in QBiPointer to know if we can safely cast parent.asT1() to QQmlVMEMetaObject*
- parent.setFlagValue(QQmlData::get(obj)->hasVMEMetaObject);
- } else
- parent = obj->metaObject();
-
- op->metaObject = this;
QQmlData::get(obj)->hasVMEMetaObject = true;
int qobject_type = qMetaTypeId<QObject*>();
@@ -471,80 +585,10 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
Q_UNUSED(o);
int id = _id;
- if (c == QMetaObject::WriteProperty && interceptors &&
- !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) {
-
- for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
- if (vi->m_coreIndex != id)
- continue;
-
- int valueIndex = vi->m_valueTypeCoreIndex;
- int type = QQmlData::get(object)->propertyCache->property(id)->propType;
-
- if (type != QVariant::Invalid) {
- if (valueIndex != -1) {
- QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
- 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->metaObject()->property(valueIndex);
- QVariant newValue(type, a[0]);
- valueType->read(object, id);
- QVariant prevComponentValue = valueProp.read(valueType);
-
- valueType->setValue(newValue);
- QVariant newComponentValue = valueProp.read(valueType);
+ if (intercept(c, _id, a))
+ return -1;
- // 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, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
-
- vi->write(newComponentValue);
- updated = true;
- }
-
- if (updated)
- return -1;
- } else {
- vi->write(QVariant(type, a[0]));
- return -1;
- }
- }
- }
- }
if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) {
if (id >= propOffset()) {
id -= propOffset();
@@ -992,14 +1036,6 @@ void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop)
static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0);
}
-void QQmlVMEMetaObject::registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor)
-{
- interceptor->m_coreIndex = index;
- interceptor->m_valueTypeCoreIndex = valueIndex;
- interceptor->m_next = interceptors;
- interceptors = interceptor;
-}
-
quint16 QQmlVMEMetaObject::vmeMethodLineNumber(int index)
{
if (index < methodOffset()) {
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index af0742d2c6..7da44e3b82 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -148,17 +148,57 @@ public:
int m_index;
};
+
+class Q_QML_PRIVATE_EXPORT QQmlInterceptorMetaObject : public QAbstractDynamicMetaObject
+{
+public:
+ QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache);
+ ~QQmlInterceptorMetaObject();
+
+ void registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor);
+
+ static QQmlInterceptorMetaObject *get(QObject *obj);
+
+ virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o);
+
+ // Used by auto-tests for inspection
+ QQmlPropertyCache *propertyCache() const { return cache; }
+
+protected:
+ virtual int metaCall(QObject *o, QMetaObject::Call c, int id, void **a);
+ bool intercept(QMetaObject::Call c, int id, void **a);
+
+public:
+ QObject *object;
+ QQmlPropertyCache *cache;
+ QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent;
+
+ QQmlPropertyValueInterceptor *interceptors;
+ bool hasAssignedMetaObjectData;
+};
+
+inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj)
+{
+ if (obj) {
+ if (QQmlData *data = QQmlData::get(obj)) {
+ if (data->hasInterceptorMetaObject)
+ return static_cast<QQmlInterceptorMetaObject *>(QObjectPrivate::get(obj)->metaObject);
+ }
+ }
+
+ return 0;
+}
+
class QQmlVMEVariant;
class QQmlRefCount;
class QQmlVMEMetaObjectEndpoint;
-class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QAbstractDynamicMetaObject
+class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
{
public:
QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, const QQmlVMEMetaData *data);
~QQmlVMEMetaObject();
bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const;
- void registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor);
QV4::ReturnedValue vmeMethod(int index);
quint16 vmeMethodLineNumber(int index);
void setVmeMethod(int index, const QV4::Value &function);
@@ -167,11 +207,6 @@ public:
void connectAliasSignal(int index, bool indexInSignalRange);
- virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o);
-
- // Used by auto-tests for inspection
- QQmlPropertyCache *propertyCache() const { return cache; }
-
static inline QQmlVMEMetaObject *get(QObject *o);
static QQmlVMEMetaObject *getForProperty(QObject *o, int coreIndex);
static QQmlVMEMetaObject *getForMethod(QObject *o, int coreIndex);
@@ -185,9 +220,7 @@ public:
friend class QQmlVMEVariantQObjectPtr;
friend class QQmlPropertyCache;
- QObject *object;
QQmlGuardedContextData ctxt;
- QQmlPropertyCache *cache;
const QQmlVMEMetaData *metaData;
inline int propOffset() const;
@@ -195,7 +228,6 @@ public:
inline int signalOffset() const;
inline int signalCount() const;
- bool hasAssignedMetaObjectData;
QQmlVMEMetaObjectEndpoint *aliasEndpoints;
QV4::WeakValue properties;
@@ -233,8 +265,6 @@ public:
void connectAlias(int aliasId);
- QQmlPropertyValueInterceptor *interceptors;
-
QV4::PersistentValue *methods;
QV4::ReturnedValue method(int);
@@ -243,8 +273,6 @@ public:
QVariant readPropertyAsVariant(int);
void writeProperty(int, const QVariant &);
- QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent;
-
inline QQmlVMEMetaObject *parentVMEMetaObject() const;
void listChanged(int);
diff --git a/tests/auto/quick/qquickbehaviors/data/Accelerator.qml b/tests/auto/quick/qquickbehaviors/data/Accelerator.qml
new file mode 100644
index 0000000000..a2b5146c3f
--- /dev/null
+++ b/tests/auto/quick/qquickbehaviors/data/Accelerator.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.3
+
+Rectangle {
+ property alias value: range.width
+ color: "yellow"
+ Text {
+ text: 'value: ' + value
+ }
+
+ Rectangle {
+ id: range
+ objectName: "range"
+ color: "red"
+ width: 0
+ height: 5
+ anchors.bottom: parent.bottom
+ }
+}
diff --git a/tests/auto/quick/qquickbehaviors/data/aliased.qml b/tests/auto/quick/qquickbehaviors/data/aliased.qml
new file mode 100644
index 0000000000..e65e035d83
--- /dev/null
+++ b/tests/auto/quick/qquickbehaviors/data/aliased.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.3
+
+Rectangle {
+ width: 400
+ height: 400
+ id: rect
+ property bool accelerating : false
+
+ Text {
+ anchors.centerIn: parent
+ text: "Press anywere to accelerate"
+ }
+
+ Accelerator {
+ id: acc
+ objectName: "acc"
+ anchors.fill: parent
+ value: accelerating ? 400 : 0
+ Behavior on value {
+ NumberAnimation {
+ duration: 500
+ }
+ }
+ }
+
+ MouseArea {
+ id: clicker
+ anchors.fill: parent
+ }
+
+ states: State {
+ name: "moved"
+ when: clicker.pressed
+ PropertyChanges {
+ target: rect
+ accelerating: true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
index 643bed4376..635958314f 100644
--- a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
+++ b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
@@ -76,6 +76,7 @@ private slots:
void multipleChangesToValueType();
void currentValue();
void disabledWriteWhileRunning();
+ void aliasedProperty();
};
void tst_qquickbehaviors::simpleBehavior()
@@ -576,6 +577,23 @@ void tst_qquickbehaviors::disabledWriteWhileRunning()
}
}
+void tst_qquickbehaviors::aliasedProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("aliased.qml"));
+ QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create()));
+ QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
+
+ QQuickItemPrivate::get(rect.data())->setState("moved");
+ QScopedPointer<QQuickRectangle> acc(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("acc")));
+ QScopedPointer<QQuickRectangle> range(qobject_cast<QQuickRectangle*>(acc->findChild<QQuickRectangle*>("range")));
+ QTRY_VERIFY(acc->property("value").toDouble() > 0);
+ QTRY_VERIFY(range->width() > 0);
+ QTRY_VERIFY(acc->property("value").toDouble() < 400);
+ QTRY_VERIFY(range->width() < 400);
+ //i.e. the behavior has been triggered
+}
+
QTEST_MAIN(tst_qquickbehaviors)
#include "tst_qquickbehaviors.moc"