aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/types
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@qt.io>2019-04-29 10:42:00 +0200
committerMichael Brasser <michael.brasser@live.com>2019-05-17 19:28:31 +0000
commitf38e071f5b353cbf9ce6c6c104bd82099ae0aa14 (patch)
treee7b1acb1ecc513912b7e0956e85ec00185405973 /src/qml/types
parentbc9f502d5ff7b6fc874f0e65019f7967916eab11 (diff)
Restore value bindings when disabling a Binding element
We previously only restored script bindings that were replaced by a Binding. Now we handle both. [ChangeLog] QML Binding elements now support restoring previous values of the bound property when the binding is disabled. This will be the default behavior in Qt 5.15. Reliance on the old behavior of only restoring binding, not literal values results in a warning now. Fixes: QTBUG-33444 Change-Id: I833403b0645c08eee486fbd4acf5d3c7de2ef73a Reviewed-by: Michael Brasser <michael.brasser@live.com>
Diffstat (limited to 'src/qml/types')
-rw-r--r--src/qml/types/qqmlbind.cpp142
-rw-r--r--src/qml/types/qqmlbind_p.h18
2 files changed, 155 insertions, 5 deletions
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index 513f7f2997..1ba015f796 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -43,6 +43,8 @@
#include <private/qqmlproperty_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qv4persistent_p.h>
#include <qqmlengine.h>
#include <qqmlcontext.h>
@@ -60,7 +62,21 @@ QT_BEGIN_NAMESPACE
class QQmlBindPrivate : public QObjectPrivate
{
public:
- QQmlBindPrivate() : obj(nullptr), componentComplete(true), delayed(false), pendingEval(false) {}
+ QQmlBindPrivate()
+ : obj(nullptr)
+ , prevBind(QQmlAbstractBinding::Ptr())
+ , prevIsVariant(false)
+ , componentComplete(true)
+ , delayed(false)
+ , pendingEval(false)
+ , restoreBinding(true)
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ , restoreValue(false)
+ , restoreModeExplicit(false)
+#else
+ , restoreValue(true)
+#endif
+ {}
~QQmlBindPrivate() { }
QQmlNullableValue<bool> when;
@@ -69,11 +85,20 @@ public:
QQmlNullableValue<QVariant> value;
QQmlProperty prop;
QQmlAbstractBinding::Ptr prevBind;
+ QV4::PersistentValue v4Value;
+ QVariant prevValue;
+ bool prevIsVariant:1;
bool componentComplete:1;
bool delayed:1;
bool pendingEval:1;
+ bool restoreBinding:1;
+ bool restoreValue:1;
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ bool restoreModeExplicit:1;
+#endif
void validate(QObject *binding) const;
+ void clearPrev();
};
void QQmlBindPrivate::validate(QObject *binding) const
@@ -323,6 +348,57 @@ void QQmlBind::setDelayed(bool delayed)
eval();
}
+/*!
+ \qmlproperty enumeration QtQml::Binding::restoreMode
+ \since 5.14
+
+ This property can be used to describe if and how the original value should
+ be restored when the binding is disabled.
+
+ The possible values are:
+ \list
+ \li Binding.RestoreNone The original value is not restored at all
+ \li Binding.RestoreBinding The original value is restored if it was another
+ binding. In that case the old binding is in effect again.
+ \li Binding.RestoreValue The original value is restored if it was a plain
+ value rather than a binding.
+ \li Binding.RestoreBindingOrValue The original value is always restored.
+ \list
+
+ \warning The default value is Binding.RestoreBinding. This will change in
+ Qt 5.15 to Binding.RestoreBindingOrValue.
+
+ If you rely on any specific behavior regarding the restoration of plain
+ values when bindings get disabled you should migrate to explicitly set the
+ restoreMode.
+
+ Reliance on a restoreMode that doesn't restore the previous binding or value
+ for a specific property results in a run-time warning.
+*/
+QQmlBind::RestorationMode QQmlBind::restoreMode() const
+{
+ Q_D(const QQmlBind);
+ unsigned result = RestoreNone;
+ if (d->restoreValue)
+ result |= RestoreValue;
+ if (d->restoreBinding)
+ result |= RestoreBinding;
+ return RestorationMode(result);
+}
+
+void QQmlBind::setRestoreMode(RestorationMode newMode)
+{
+ Q_D(QQmlBind);
+ if (newMode != restoreMode()) {
+ d->restoreValue = (newMode & RestoreValue);
+ d->restoreBinding = (newMode & RestoreBinding);
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ d->restoreModeExplicit = true;
+#endif
+ emit restoreModeChanged();
+ }
+}
+
void QQmlBind::setTarget(const QQmlProperty &p)
{
Q_D(QQmlBind);
@@ -358,6 +434,14 @@ void QQmlBind::prepareEval()
}
}
+void QQmlBindPrivate::clearPrev()
+{
+ prevBind = nullptr;
+ v4Value.clear();
+ prevValue.clear();
+ prevIsVariant = false;
+}
+
void QQmlBind::eval()
{
Q_D(QQmlBind);
@@ -369,16 +453,64 @@ void QQmlBind::eval()
if (!d->when) {
//restore any previous binding
if (d->prevBind) {
- QQmlAbstractBinding::Ptr p = d->prevBind;
- d->prevBind = nullptr;
- QQmlPropertyPrivate::setBinding(p.data());
+ if (d->restoreBinding) {
+ 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) {
+ auto propPriv = QQmlPropertyPrivate::get(d->prop);
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object);
+ Q_ASSERT(vmemo);
+ vmemo->setVMEProperty(propPriv->core.coreIndex(), *d->v4Value.valueRef());
+ d->clearPrev();
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ } else if (!d->restoreModeExplicit) {
+ qmlWarning(this)
+ << "Not restoring previous value because restoreMode has not been set."
+ << "This behavior is deprecated."
+ << "In Qt < 5.15 the default is Binding.RestoreBinding."
+ << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue.";
+#endif
+ }
+ } else if (d->prevIsVariant) {
+ if (d->restoreValue) {
+ d->prop.write(d->prevValue);
+ d->clearPrev();
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ } else if (!d->restoreModeExplicit) {
+ qmlWarning(this)
+ << "Not restoring previous value because restoreMode has not been set."
+ << "This behavior is deprecated."
+ << "In Qt < 5.15 the default is Binding.RestoreBinding."
+ << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue.";
+#endif
+ }
}
return;
}
//save any set binding for restoration
- if (!d->prevBind)
+ if (!d->prevBind && d->v4Value.isEmpty() && !d->prevIsVariant) {
+ // try binding first
d->prevBind = QQmlPropertyPrivate::binding(d->prop);
+
+ if (!d->prevBind) { // nope, try a V4 value next
+ auto propPriv = QQmlPropertyPrivate::get(d->prop);
+ auto propData = propPriv->core;
+ if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) {
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object);
+ Q_ASSERT(vmemo);
+ auto retVal = vmemo->vmeProperty(propData.coreIndex());
+ d->v4Value = QV4::PersistentValue(vmemo->engine, retVal);
+ } else { // nope, use the meta object to get a QVariant
+ d->prevValue = d->prop.read();
+ d->prevIsVariant = true;
+ }
+ }
+ }
+
QQmlPropertyPrivate::removeBinding(d->prop);
}
diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h
index 5bf9ef85c6..22007a3d25 100644
--- a/src/qml/types/qqmlbind_p.h
+++ b/src/qml/types/qqmlbind_p.h
@@ -60,6 +60,15 @@ QT_BEGIN_NAMESPACE
class QQmlBindPrivate;
class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus
{
+public:
+ enum RestorationMode {
+ RestoreNone = 0x0,
+ RestoreBinding = 0x1,
+ RestoreValue = 0x2,
+ RestoreBindingOrValue = RestoreBinding | RestoreValue
+ };
+
+private:
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlBind)
Q_INTERFACES(QQmlParserStatus)
@@ -69,6 +78,9 @@ class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSourc
Q_PROPERTY(QVariant value READ value WRITE setValue)
Q_PROPERTY(bool when READ when WRITE setWhen)
Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8)
+ Q_PROPERTY(RestorationMode restoreMode READ restoreMode WRITE setRestoreMode
+ NOTIFY restoreModeChanged REVISION 14)
+ Q_ENUM(RestorationMode)
public:
QQmlBind(QObject *parent=nullptr);
@@ -89,6 +101,12 @@ public:
bool delayed() const;
void setDelayed(bool);
+ RestorationMode restoreMode() const;
+ void setRestoreMode(RestorationMode);
+
+Q_SIGNALS:
+ void restoreModeChanged();
+
protected:
void setTarget(const QQmlProperty &) override;
void classBegin() override;