aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp16
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp1
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h5
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp5
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/aliasOfQProperty.qml7
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp37
7 files changed, 68 insertions, 5 deletions
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 45c9400934..43c639ce8d 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -890,10 +890,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
_scopeObject, runtimeFunction, currentQmlContext());
if (bindingProperty->isBindable()) {
+ auto target = _bindingTarget;
+ if (bindingProperty->isAlias()) {
+ // If the property is an alias, we cannot obtain the bindable interface directly with qt_metacall
+ // so instead, we resolve the alias to obtain the actual target
+ // This should be faster than doing a detour through the metaobject of the target, and relying on
+ // QMetaObject::metacall doing the correct resolution
+ QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1);
+ QQmlPropertyIndex propIndex;
+ QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
+ QQmlData *data = QQmlData::get(target);
+ Q_ASSERT(data && data->propertyCache);
+ bindingProperty = data->propertyCache->property(propIndex.coreIndex());
+ }
auto &observer = QQmlData::get(_scopeObject)->propertyObservers.emplace_back(expr);
QUntypedBindable bindable;
void *argv[] = { &bindable };
- _bindingTarget->qt_metacall(QMetaObject::BindableProperty, bindingProperty->coreIndex(), argv);
+ target->qt_metacall(QMetaObject::BindableProperty, bindingProperty->coreIndex(), argv);
+ Q_ASSERT(bindable.isValid());
bindable.observe(&observer);
} else {
QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine);
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 3c6c0c82de..4f557527c9 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -1147,6 +1147,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
property.setReadable(true);
property.setWritable(data->isWritable());
property.setResettable(data->isResettable());
+ property.setBindable(data->isBindable());
}
for (int ii = 0; ii < methods.count(); ++ii) {
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 626b274c2f..21551db5c4 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -785,6 +785,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
*type = 0;
bool writable = false;
bool resettable = false;
+ bool bindable = false;
propertyFlags->setIsAlias(true);
@@ -861,13 +862,14 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
*type = targetProperty->propType();
writable = targetProperty->isWritable();
resettable = targetProperty->isResettable();
-
+ bindable = targetProperty->isBindable();
} else {
// value type or primitive type or enum
*type = targetProperty->propType();
writable = targetProperty->isWritable();
resettable = targetProperty->isResettable();
+ bindable = targetProperty->isBindable();
if (valueTypeIndex != -1) {
const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
@@ -891,6 +893,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable);
propertyFlags->setIsResettable(resettable);
+ propertyFlags->setIsBindable(bindable);
return QQmlError();
}
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index ca8a24439b..eb3b0e43ac 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -382,7 +382,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
QString finalSignalHandlerPropertyName = signalNameCandidate;
uint flags = QV4::CompiledData::Binding::IsSignalHandlerExpression;
- if (signal) {
+ const bool isPropertyObserver = !signalPropertyData && qPropertyData && qPropertyData->isBindable();
+ if (signal && !(qPropertyData && qPropertyData->isAlias() && isPropertyObserver)) {
int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex());
sigIndex = propertyCache->originalClone(sigIndex);
@@ -400,7 +401,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
}
parameters += param;
}
- } else if (!signalPropertyData && qPropertyData && qPropertyData->isBindable()) {
+ } else if (isPropertyObserver) {
finalSignalHandlerPropertyName = qPropertyName;
flags = QV4::CompiledData::Binding::IsPropertyObserver;
} else {
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 2d0f131477..6919c6dba3 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -687,7 +687,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
const int signalCount = compiledObject ? int(compiledObject->nSignals) : 0;
const int methodCount = compiledObject ? int(compiledObject->nFunctions) : 0;
- if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) {
+ if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty || c == QMetaObject::BindableProperty) {
if (id >= propOffset()) {
id -= propOffset();
diff --git a/tests/auto/qml/qqmlecmascript/data/aliasOfQProperty.qml b/tests/auto/qml/qqmlecmascript/data/aliasOfQProperty.qml
new file mode 100644
index 0000000000..6c37a33f39
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/aliasOfQProperty.qml
@@ -0,0 +1,7 @@
+import Qt.test 1.0
+ClassWithQProperty {
+ id: root
+ property alias myAlias: root.value
+ property int changeCounter: 0
+ onMyAliasChanged: ++changeCounter
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index ef2eb5ad03..e3396a430f 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -382,6 +382,7 @@ private slots:
void semicolonAfterProperty();
void hugeStack();
void bindingOnQProperty();
+ void aliasOfQProperty();
void bindingOnQPropertyContextProperty();
void bindingContainingQProperty();
void urlConstruction();
@@ -9177,6 +9178,42 @@ void tst_qqmlecmascript::bindingOnQProperty()
QVERIFY(qprop.hasBinding());
}
+void tst_qqmlecmascript::aliasOfQProperty() {
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("aliasOfQProperty.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> root(component.create());
+ QProperty<float> &qprop = static_cast<ClassWithQProperty*>(root.data())->value;
+ QProperty<float> otherProperty;
+
+
+ qprop.setBinding([&](){return otherProperty.value();});
+ // changing the target properties value triggers the alias property's change handler exactly
+ // once, and doesn't require reading the value (binding is no longer lazy)
+ otherProperty.setValue(42.0);
+ QCOMPARE(root->property("changeCounter").toInt(), 1);
+ // reading the value afterwards doesn't trigger the observer again
+ QCOMPARE(root->property("myAlias").toFloat(), 42.0);
+ QCOMPARE(root->property("changeCounter").toInt(), 1);
+
+ // writing to the alias breaks the binding
+ root->setProperty("myAlias", 12.0);
+ QCOMPARE(qprop.value(), 12.0);
+ QVERIFY(!qprop.hasBinding());
+
+ // it is possible to obtain the bindable interface of the target from the bindable property
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ int myaliasPropertyIndex = root->metaObject()->indexOfProperty("myAlias");
+ root->metaObject()->metacall(root.get(), QMetaObject::BindableProperty, myaliasPropertyIndex, argv);
+ QVERIFY(bindable.isValid());
+
+ bool ok = bindable.setBinding(Qt::makePropertyBinding([&]() -> float {return 13.0; } ));
+ QVERIFY(ok);
+ QVERIFY(qprop.hasBinding());
+ QCOMPARE(qprop.value(), 13.0);
+}
+
void tst_qqmlecmascript::bindingOnQPropertyContextProperty()
{
QSKIP("Test needs to be adjusted");