diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2021-02-09 16:24:31 +0100 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2021-05-25 16:09:05 +0200 |
commit | e3c9fdfffb2f62da90938e8e5e76f1d15f30385d (patch) | |
tree | a0c929025f698b86055e3ff8ebf34beb84184455 /tests | |
parent | 14b8fc630f2a77b1382136e6943f2cafb44bd154 (diff) |
Behavior: intercept bindings
Use the new API in QQmlPropertyValueInterceptor and
QQmlInterceptorMetaObject to acutally intercept bindable properties in
Behavior.
This works as follows:
We intercept the bindable metacall, and construct an untyped proxy
property. The proxy property has storage for the property data
(currently via constructing a QVariant for it). It also has its own
binding data. We install an observer on the proxy binding data which
notifies us whenever the proxy changes. That observer is actually the
QQuickBehaviorPrivate (which now inherits QPropertyObserver). Whenever
the observer triggers, we read the current value of the proxy property
and call QQuickBehavior::write with it. That's how Behavior can now
track updates. As binding updates end up calling the write method of the
Behavior, we get support for toggling enabled for free.
The final part of the puzzle is how to get the property system to use
the proxy property instead of the real property. This is done when we
intercept the Bindable metacall: Instead of returning the source's
QUntypedBindable, we construct a custom one. Its functions do the
following:
- setting/getting values and bindings do not affect the source, but
instead operate on the proxy
- observers are still installed on the source; that way, they see all
writes done by the interceptor, instead of only the direct writes to
the source property
- makeBinding forwards to the source
- We make use of the metatype multiplexing hack in the getter
Task-number: QTBUG-90999
Change-Id: Ib91a12b05975f1257026ba4d2b64ec14852d4a14
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Diffstat (limited to 'tests')
4 files changed, 95 insertions, 1 deletions
diff --git a/tests/auto/quick/qquickbehaviors/CMakeLists.txt b/tests/auto/quick/qquickbehaviors/CMakeLists.txt index 330df778b6..b864b0c21b 100644 --- a/tests/auto/quick/qquickbehaviors/CMakeLists.txt +++ b/tests/auto/quick/qquickbehaviors/CMakeLists.txt @@ -14,6 +14,7 @@ qt_internal_add_test(tst_qquickbehaviors SOURCES ../../shared/util.cpp ../../shared/util.h tst_qquickbehaviors.cpp + bindable.h INCLUDE_DIRECTORIES ../../shared PUBLIC_LIBRARIES @@ -25,6 +26,14 @@ qt_internal_add_test(tst_qquickbehaviors TESTDATA ${test_data} ) +set_target_properties(tst_qquickbehaviors PROPERTIES + QT_QML_MODULE_VERSION 1.0 + QT_QML_MODULE_URI test +) + +qt6_qml_type_registration(tst_qquickbehaviors) + + #### Keys ignored in scope 1:.:.:qquickbehaviors.pro:<TRUE>: # DISTFILES = "data/*" diff --git a/tests/auto/quick/qquickbehaviors/bindable.h b/tests/auto/quick/qquickbehaviors/bindable.h new file mode 100644 index 0000000000..ebdf8711a3 --- /dev/null +++ b/tests/auto/quick/qquickbehaviors/bindable.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** 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 BINDABLE_H +#define BINDABLE_H + +#include <QObject> +#include <QQuickItem> +#include <qqmlregistration.h> + +class TestBindable : public QQuickItem +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int prop READ prop WRITE setProp BINDABLE bindableProp) +public: + int prop() { return m_prop; } + void setProp(int i) { m_prop = i; } + QBindable<int> bindableProp() { return &m_prop; } + +private: + QProperty<int> m_prop; +}; +#endif diff --git a/tests/auto/quick/qquickbehaviors/data/bindableProperty.qml b/tests/auto/quick/qquickbehaviors/data/bindableProperty.qml new file mode 100644 index 0000000000..e4d76036aa --- /dev/null +++ b/tests/auto/quick/qquickbehaviors/data/bindableProperty.qml @@ -0,0 +1,12 @@ +import QtQuick +import test + +TestBindable { + property int targetValue: 0 + prop: targetValue + property alias enableBehavior: behavior.enabled + Behavior on prop { + id: behavior + NumberAnimation {duration: 100} + } +} diff --git a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp index 3b46019f64..35423651f9 100644 --- a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp +++ b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,6 +37,7 @@ #include <QtQuick/private/qquicksmoothedanimation_p.h> #include <private/qquickitem_p.h> #include "../../shared/util.h" +#include "bindable.h" class tst_qquickbehaviors : public QQmlDataTest { @@ -76,6 +77,7 @@ private slots: void oneWay(); void safeToDelete(); void targetProperty(); + void bindableProperty(); }; void tst_qquickbehaviors::simpleBehavior() @@ -678,6 +680,29 @@ void tst_qquickbehaviors::targetProperty() QCOMPARE(item->property("emptyBehaviorName").toString(), ""); } +void tst_qquickbehaviors::bindableProperty() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("bindableProperty.qml")); + QScopedPointer<QObject> root(c.create()); + QVERIFY2(root, qPrintable(c.errorString())); + auto testBindable = qobject_cast<TestBindable *>(root.get()); + QVERIFY(testBindable); + + testBindable->setProperty("targetValue", 100); + QVERIFY(testBindable->prop() != 100); + QTRY_COMPARE(testBindable->prop(), 100); + + testBindable->setProperty("enableBehavior", false); + testBindable->setProperty("targetValue", 200); + QCOMPARE(testBindable->prop(), 200); + + testBindable->setProperty("enableBehavior", true); + testBindable->setProperty("prop", 300); // write through metaobject system gets intercepted + QVERIFY(testBindable->prop() != 300); + QTRY_COMPARE(testBindable->prop(), 300); +} + QTEST_MAIN(tst_qquickbehaviors) |