aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2
-rw-r--r--src/qml/qml/qqmlbinding.cpp2
-rw-r--r--src/qml/qml/qqmlengine_p.h15
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp57
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h4
-rw-r--r--tests/auto/qml/qqmlecmascript/data/bindingContainingQProperty.qml5
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp12
7 files changed, 91 insertions, 6 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index b30d88359b..ee3be6fcdc 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -261,7 +261,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (ep && ep->propertyCapture && !property->isConstant() && !property->isBindable())
+ if (ep && ep->propertyCapture && !property->isConstant())
ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 3253c547ab..6336b2cedc 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -670,7 +670,7 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
bool QQmlBinding::hasDependencies() const
{
- return !activeGuards.isEmpty() || translationsCaptured();
+ return !activeGuards.isEmpty() || translationsCaptured() || qpropertyChangeTriggers;
}
class QObjectPointerBinding: public QQmlNonbindingBinding
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index f8b8b187a5..1f513ef8cc 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -81,6 +81,8 @@
#include <private/qjsengine_p.h>
#include <private/qqmldirparser_p.h>
+#include <qproperty.h>
+
QT_BEGIN_NAMESPACE
class QQmlContext;
@@ -125,6 +127,18 @@ public:
QQmlJavaScriptExpressionGuard *next;
};
+struct QPropertyChangeTrigger {
+ QQmlJavaScriptExpression * m_expression;
+ void operator()();
+};
+
+struct TriggerList : QPropertyChangeHandler<QPropertyChangeTrigger> {
+ TriggerList(QPropertyChangeTrigger trigger) : QPropertyChangeHandler<QPropertyChangeTrigger>(trigger) {};
+ TriggerList *next = nullptr;
+ QObject *target = nullptr;
+ int propertyIndex = 0;
+};
+
class Q_QML_PRIVATE_EXPORT QQmlEnginePrivate : public QJSEnginePrivate
{
Q_DECLARE_PUBLIC(QQmlEngine)
@@ -140,6 +154,7 @@ public:
QQmlPropertyCapture *propertyCapture;
QRecyclePool<QQmlJavaScriptExpressionGuard> jsExpressionGuardPool;
+ QRecyclePool<TriggerList> qPropertyTriggerPool;
QQmlContext *rootContext;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 0f83baa893..88a9932f53 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qqmljavascriptexpression_p.h"
+#include "qqmljavascriptexpression_p.h"
#include <private/qqmlexpression_p.h>
#include <private/qv4context_p.h>
@@ -51,6 +52,9 @@
#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmlsourcecoordinate_p.h>
+#include <private/qqmlabstractbinding_p.h>
+#include <private/qqmlpropertybinding_p.h>
+#include <private/qproperty_p.h>
QT_BEGIN_NAMESPACE
@@ -109,6 +113,12 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
m_nextExpression->m_prevExpression = m_prevExpression;
}
+ while (qpropertyChangeTriggers) {
+ auto current = qpropertyChangeTriggers;
+ qpropertyChangeTriggers = current->next;
+ QRecyclePool<TriggerList>::Delete(current);
+ }
+
clearActiveGuards();
clearError();
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
@@ -289,7 +299,33 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotif
return;
Q_ASSERT(expression);
+ const QQmlData *ddata = QQmlData::get(o, /*create=*/false);
+ bool isBindable = false;
+ if (auto const propCache = ddata ? ddata->propertyCache : nullptr; propCache) {
+ Q_ASSERT(propCache->property(c));
+ isBindable = propCache->property(c)->isBindable();
+ } else {
+ auto metaProp = o->staticMetaObject.property(c);
+ isBindable = metaProp.isBindable();
+ }
+ if (isBindable) {
+ // if the property is a QPropery, and we're binding to a QProperty
+ // the automatic capturing process already takes care of everything
+ if (typeid(QQmlPropertyBinding) == typeid(*expression))
+ return;
+ for (auto trigger = expression->qpropertyChangeTriggers; trigger; trigger = trigger->next) {
+ if (trigger->target == o && trigger->propertyIndex == c)
+ return; // already installed
+ }
+ auto trigger = expression->allocatePropertyChangeTrigger(o, c);
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ o->qt_metacall(QMetaObject::BindableProperty, c, argv);
+ bindable.observe(trigger);
+ return;
+ }
if (n == -1) {
+
if (!errorString) {
errorString = new QStringList;
QString preamble = QLatin1String("QQmlExpression: Expression ") +
@@ -298,11 +334,9 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotif
errorString->append(preamble);
}
- const QMetaObject *metaObj = o->metaObject();
- QMetaProperty metaProp = metaObj->property(c);
-
+ const QMetaProperty metaProp = o->metaObject()->property(c);
QString error = QLatin1String(" ") +
- QString::fromUtf8(metaObj->className()) +
+ QString::fromUtf8(o->metaObject()->className()) +
QLatin1String("::") +
QString::fromUtf8(metaProp.name());
errorString->append(error);
@@ -413,6 +447,21 @@ void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::Exec
m_compilationUnit = compilationUnit;
}
+void QPropertyChangeTrigger::operator()() {
+ m_expression->expressionChanged();
+}
+
+QPropertyChangeHandler<QPropertyChangeTrigger> *QQmlJavaScriptExpression::allocatePropertyChangeTrigger(QObject *target, int propertyIndex)
+{
+ auto trigger = QQmlEnginePrivate::get(engine())->qPropertyTriggerPool.New(QPropertyChangeTrigger { this });
+ trigger->target = target;
+ trigger->propertyIndex = propertyIndex;
+ auto oldHead = qpropertyChangeTriggers;
+ trigger->next = oldHead;
+ qpropertyChangeTriggers = trigger;
+ return trigger;
+}
+
void QQmlJavaScriptExpression::clearActiveGuards()
{
while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst())
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index c2df5004d1..a1e06e34da 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -164,6 +164,7 @@ public:
QQmlEngine *engine() const { return m_context ? m_context->engine() : nullptr; }
bool hasUnresolvedNames() const { return m_context && m_context->hasUnresolvedNames(); }
+ QPropertyChangeHandler<QPropertyChangeTrigger>* allocatePropertyChangeTrigger(QObject *target, int propertyIndex);
protected:
void createQmlBinding(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope,
@@ -216,6 +217,9 @@ private:
QV4::PersistentValue m_qmlScope;
QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit;
QV4::Function *m_v4Function;
+
+protected:
+ TriggerList *qpropertyChangeTriggers = nullptr;
};
class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture
diff --git a/tests/auto/qml/qqmlecmascript/data/bindingContainingQProperty.qml b/tests/auto/qml/qqmlecmascript/data/bindingContainingQProperty.qml
new file mode 100644
index 0000000000..52b7122ef0
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/bindingContainingQProperty.qml
@@ -0,0 +1,5 @@
+import Qt.test 1.0
+
+ClassWithQProperty {
+ property real expected: value
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index c37d2b0990..ef2eb5ad03 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -383,6 +383,7 @@ private slots:
void hugeStack();
void bindingOnQProperty();
void bindingOnQPropertyContextProperty();
+ void bindingContainingQProperty();
void urlConstruction();
void urlPropertyInvalid();
void urlPropertySet();
@@ -9189,6 +9190,17 @@ void tst_qqmlecmascript::bindingOnQPropertyContextProperty()
// QCOMPARE(classWithQProperty->value.value(), 2);
}
+void tst_qqmlecmascript::bindingContainingQProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("bindingContainingQProperty.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> test(component.create());
+ QVERIFY(!test.isNull());
+ test->setProperty("value", 42.0);
+ QCOMPARE(test->property("expected"), 42.0);
+}
+
void tst_qqmlecmascript::urlConstruction()
{
QQmlEngine qmlengine;