aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2021-01-21 20:02:17 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2021-02-03 09:28:40 +0100
commit0deb3961e6b0a1f15a491f7a19085df3a7a506b1 (patch)
tree28c9db4fd4bd0a334a1848d852f6394f83bc97d9
parentb322a971f06823a4356f2b3aa331501aa4d0dc7f (diff)
QmlBind: support bindable properties
This patch ensures that the QML Binding element can also save and restore C++ bindings. Should QQuickItem's x and y property be ported to the new property system, we'd need new test cases to verify that "old-style" bindings are still handled correctly. This task is however left for the change porting the properties. Task-number: QTBUG-90493 Change-Id: I506ffa1060ff32a7d722214e5ccd469bdaa61ff8 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/types/qqmlbind.cpp49
-rw-r--r--tests/auto/qml/qqmlbinding/CMakeLists.txt9
-rw-r--r--tests/auto/qml/qqmlbinding/WithBindableProperties.h55
-rw-r--r--tests/auto/qml/qqmlbinding/data/restoreBinding5.qml24
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp30
5 files changed, 158 insertions, 9 deletions
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index 6d762401d0..8fc26a2550 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -83,7 +83,10 @@ public:
QString propName;
QQmlNullableValue<QJSValue> value;
QQmlProperty prop;
- QQmlAbstractBinding::Ptr prevBind;
+ union {
+ QQmlAbstractBinding::Ptr prevBind;
+ QUntypedPropertyBinding prevPropertyBinding;
+ };
QV4::PersistentValue v4Value;
QVariant prevValue;
bool prevIsVariant:1;
@@ -96,6 +99,7 @@ public:
void validate(QObject *binding) const;
void clearPrev();
+ bool prevBindingSet() const;
};
void QQmlBindPrivate::validate(QObject *binding) const
@@ -444,12 +448,23 @@ void QQmlBind::prepareEval()
void QQmlBindPrivate::clearPrev()
{
- prevBind = nullptr;
+ if (prop.property().isBindable())
+ prevPropertyBinding = {};
+ else
+ prevBind = nullptr;
v4Value.clear();
prevValue.clear();
prevIsVariant = false;
}
+bool QQmlBindPrivate::prevBindingSet() const
+{
+ if (prop.property().isBindable())
+ return !prevPropertyBinding.isNull();
+ else
+ return prevBind;
+}
+
void QQmlBind::eval()
{
Q_D(QQmlBind);
@@ -460,11 +475,18 @@ void QQmlBind::eval()
if (d->when.isValid()) {
if (!d->when) {
//restore any previous binding
- if (d->prevBind) {
+ if (d->prevBindingSet()) {
if (d->restoreBinding) {
- QQmlAbstractBinding::Ptr p = d->prevBind;
- d->clearPrev(); // Do that before setBinding(), as setBinding() may recurse.
- QQmlPropertyPrivate::setBinding(p.data());
+ QMetaProperty metaProp = d->prop.property();
+ if (metaProp.isBindable()) {
+ auto prevBinding = d->prevPropertyBinding;
+ d->clearPrev(); // Do that before setBinding(), as setBinding() may recurse.
+ metaProp.bindable(d->prop.object()).setBinding(prevBinding);
+ } else {
+ QQmlAbstractBinding::Ptr p = d->prevBind;
+ d->clearPrev(); // Do that before setBinding(), as setBinding() may recurse.
+ QQmlPropertyPrivate::setBinding(p.data());
+ }
}
} else if (!d->v4Value.isEmpty()) {
if (d->restoreValue) {
@@ -484,11 +506,18 @@ void QQmlBind::eval()
}
//save any set binding for restoration
- if (!d->prevBind && d->v4Value.isEmpty() && !d->prevIsVariant) {
+ if (!d->prevBindingSet() && d->v4Value.isEmpty() && !d->prevIsVariant) {
// try binding first
- d->prevBind = QQmlPropertyPrivate::binding(d->prop);
+ d->prop.property().isBindable();
+ QMetaProperty metaProp = d->prop.property();
+ if (metaProp.isBindable()) {
+ QUntypedBindable bindable = d->prop.property().bindable(d->prop.object());
+ d->prevPropertyBinding = bindable.takeBinding();
+ } else {
+ d->prevBind = QQmlPropertyPrivate::binding(d->prop);
+ }
- if (!d->prevBind) { // nope, try a V4 value next
+ if (!d->prevBindingSet()) { // nope, try a V4 value next
auto propPriv = QQmlPropertyPrivate::get(d->prop);
auto propData = propPriv->core;
if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) {
@@ -503,6 +532,8 @@ void QQmlBind::eval()
}
}
+ // NOTE: removeBinding has no effect on QProperty classes, but
+ // we already used takeBinding to remove it
QQmlPropertyPrivate::removeBinding(d->prop);
}
diff --git a/tests/auto/qml/qqmlbinding/CMakeLists.txt b/tests/auto/qml/qqmlbinding/CMakeLists.txt
index c95d3e6f29..6bf1668970 100644
--- a/tests/auto/qml/qqmlbinding/CMakeLists.txt
+++ b/tests/auto/qml/qqmlbinding/CMakeLists.txt
@@ -14,6 +14,7 @@ qt_internal_add_test(tst_qqmlbinding
SOURCES
../../shared/util.cpp ../../shared/util.h
tst_qqmlbinding.cpp
+ WithBindableProperties.h
INCLUDE_DIRECTORIES
../../shared
PUBLIC_LIBRARIES
@@ -25,6 +26,14 @@ qt_internal_add_test(tst_qqmlbinding
TESTDATA ${test_data}
)
+set_target_properties(tst_qqmlbinding PROPERTIES
+ QT_QML_MODULE_URI "test"
+ QT_QML_MODULE_VERSION 1.0
+)
+
+qt6_qml_type_registration(tst_qqmlbinding)
+
+
## Scopes:
#####################################################################
diff --git a/tests/auto/qml/qqmlbinding/WithBindableProperties.h b/tests/auto/qml/qqmlbinding/WithBindableProperties.h
new file mode 100644
index 0000000000..562b445df7
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/WithBindableProperties.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef WithBindableProperties_H
+#define WithBindableProperties_H
+
+#include <QObject>
+#include <qqml.h>
+
+
+class WithBindableProperties : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(int a READ a WRITE setA BINDABLE bindableA)
+ Q_PROPERTY(int b READ b WRITE setB BINDABLE bindableB)
+
+public:
+ QProperty<int> m_a;
+ QProperty<int> m_b;
+ int a() {return m_a;}
+ int b() {return m_b;}
+ void setA(int val) {m_a = val;}
+ void setB(int val) {m_b = val;}
+ QBindable<int> bindableA() {return QBindable<int>(&m_a); }
+ QBindable<int> bindableB() {return QBindable<int>(&m_b); }
+};
+
+#endif
+
diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding5.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding5.qml
new file mode 100644
index 0000000000..75926c3d6e
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/restoreBinding5.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+import test 1
+
+Rectangle {
+ width: 400
+ height: 400
+
+ WithBindableProperties {
+ id: myItem
+ objectName: "myItem"
+ a: 100 - myItem.b
+
+ Binding on a {
+ when: myItem.b > 50
+ value: myItem.b
+ }
+
+ /*NumberAnimation on y {
+ loops: Animation.Infinite
+ to: 100
+ duration: 1000
+ }*/
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index afb42453d4..bc2be554a5 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -31,6 +31,7 @@
#include <private/qqmlbind_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include "../../shared/util.h"
+#include "WithBindableProperties.h"
class tst_qqmlbinding : public QQmlDataTest
{
@@ -42,6 +43,7 @@ private slots:
void binding();
void whenAfterValue();
void restoreBinding();
+ void restoreBindingBindablePorperty();
void restoreBindingValue();
void restoreBindingVarValue();
void restoreBindingJSValue();
@@ -134,6 +136,34 @@ void tst_qqmlbinding::restoreBinding()
QCOMPARE(myItem->x(), qreal(100-49));
}
+void tst_qqmlbinding::restoreBindingBindablePorperty()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restoreBinding5.qml"));
+ QScopedPointer<QQuickRectangle> rect { qobject_cast<QQuickRectangle*>(c.create()) };
+ QVERIFY2(rect, qPrintable(c.errorString()));
+
+ auto *myItem = rect->findChild<WithBindableProperties*>("myItem");
+ QVERIFY(myItem != nullptr);
+
+ myItem->setB(25);
+ QCOMPARE(myItem->a(), qreal(100-25));
+
+ myItem->setB(13);
+ QCOMPARE(myItem->a(), qreal(100-13));
+
+ //Binding takes effect
+ myItem->setB(51);
+ QCOMPARE(myItem->a(), qreal(51));
+
+ myItem->setB(88);
+ QCOMPARE(myItem->a(), qreal(88));
+
+ //original binding restored
+ myItem->setB(49);
+ QCOMPARE(myItem->a(), qreal(100-49));
+}
+
void tst_qqmlbinding::restoreBindingValue()
{
QQmlEngine engine;