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>
This commit is contained in:
parent
b322a971f0
commit
0deb3961e6
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
#####################################################################
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}*/
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue