aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/types/qqmlbind.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/types/qqmlbind.cpp')
-rw-r--r--src/qml/types/qqmlbind.cpp162
1 files changed, 117 insertions, 45 deletions
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index a31e5ccd85..bc06d4ed51 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -1,30 +1,32 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlbind_p.h"
-#include <private/qqmlnullablevalue_p.h>
-#include <private/qqmlproperty_p.h>
+#include <private/qqmlanybinding_p.h>
#include <private/qqmlbinding_p.h>
+#include <private/qqmlcomponent_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmlnullablevalue_p.h>
+#include <private/qqmlproperty_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qv4persistent_p.h>
+#include <private/qv4qmlcontext_p.h>
+#include <private/qv4resolvedtypereference_p.h>
-#include <qqmlengine.h>
-#include <qqmlcontext.h>
-#include <qqmlproperty.h>
-#include <qqmlpropertymap.h>
-#include <qqmlinfo.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlproperty.h>
+#include <QtQml/qqmlpropertymap.h>
+
+#include <QtCore/private/qobject_p.h>
-#include <QtCore/qfile.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qtimer.h>
+#include <QtCore/qfile.h>
#include <QtCore/qloggingcategory.h>
-#include <private/qqmlanybinding_p.h>
-#include <private/qv4qmlcontext_p.h>
-#include <private/qqmlcomponent_p.h>
-
-#include <private/qobject_p.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qtimer.h>
QT_BEGIN_NAMESPACE
@@ -280,7 +282,7 @@ public:
const QV4::CompiledData::Binding *binding,
QQmlComponentPrivate::ConstructionState *immediateState);
void createDelayedValues();
- void onDelayedValueChanged(const QString &delayedName);
+ void onDelayedValueChanged(QString delayedName);
void evalDelayed();
void buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState);
};
@@ -382,7 +384,7 @@ void QQmlBindPrivate::validate(QQmlBind *q) const
The Binding type restores any previously set direct bindings on the
property.
- \sa {Qt QML}
+ \sa {Qt Qml}
*/
QQmlBind::QQmlBind(QObject *parent)
: QObject(*(new QQmlBindPrivate), parent)
@@ -428,9 +430,9 @@ void QQmlBind::setWhen(bool v)
/*!
\qmlproperty QtObject QtQml::Binding::target
- The object to be updated. You only need to use this property if you can't
- supply the binding target declaratively. The following two pieces of code
- are equivalent.
+ The object to be updated. You need to use this property if the binding target
+ does not have an \c id attribute (for example, when the target is a singleton).
+ Otherwise, the following two pieces of code are equivalent:
\qml
Binding { contactName.text: name }
@@ -463,10 +465,26 @@ void QQmlBind::setObject(QObject *obj)
eval();
d->when = true;
}
+ /* if "when" and "target" depend on the same property, we might
+ end up here before we could have updated "when". So reevaluate
+ when manually here.
+ */
+ const QQmlProperty whenProp(this, QLatin1StringView("when"));
+ const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp);
+ if (auto abstractBinding = potentialWhenBinding.asAbstractBinding()) {
+ QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding);
+ if (binding->hasValidContext()) {
+ const auto boolType = QMetaType::fromType<bool>();
+ bool when;
+ binding->evaluate(&when, boolType);
+ d->when = when;
+ }
+ }
d->obj = obj;
if (d->componentComplete) {
setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this)));
- d->validate(this);
+ if (d->when)
+ d->validate(this);
}
eval();
}
@@ -520,7 +538,8 @@ void QQmlBind::setProperty(const QString &p)
d->propName = p;
if (d->componentComplete) {
setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this)));
- d->validate(this);
+ if (d->when)
+ d->validate(this);
}
eval();
}
@@ -625,14 +644,13 @@ void QQmlBind::setDelayed(bool delayed)
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.
- \endlist
+
+ \value Binding.RestoreNone The original value is not restored at all
+ \value Binding.RestoreBinding The original value is restored if it was another binding.
+ In that case the old binding is in effect again.
+ \value Binding.RestoreValue The original value is restored if it was a plain
+ value rather than a binding.
+ \value Binding.RestoreBindingOrValue The original value is always restored.
The default value is \c Binding.RestoreBindingOrValue.
@@ -720,6 +738,20 @@ static QQmlAnyBinding createBinding(
return QQmlAnyBinding();
}
+static void initCreator(
+ QQmlData::DeferredData *deferredData,
+ const QQmlRefPointer<QQmlContextData> &contextData,
+ QQmlComponentPrivate::ConstructionState *immediateState)
+{
+ if (!immediateState->hasCreator()) {
+ immediateState->setCompletePending(true);
+ immediateState->initCreator(
+ deferredData->context->parent(), deferredData->compilationUnit,
+ contextData);
+ immediateState->creator()->beginPopulateDeferred(deferredData->context);
+ }
+}
+
void QQmlBindPrivate::decodeBinding(
QQmlBind *q, const QString &propertyPrefix,
QQmlData::DeferredData *deferredData,
@@ -728,12 +760,56 @@ void QQmlBindPrivate::decodeBinding(
{
const QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit
= deferredData->compilationUnit;
- const QString propertyName = propertyPrefix
- + compilationUnit->stringAt(binding->propertyNameIndex);
+ const QString propertySuffix = compilationUnit->stringAt(binding->propertyNameIndex);
+ const QString propertyName = propertyPrefix + propertySuffix;
switch (binding->type()) {
- case QV4::CompiledData::Binding::Type_GroupProperty:
- case QV4::CompiledData::Binding::Type_AttachedProperty: {
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ if (propertyPrefix.isEmpty()) {
+ // Top-level attached properties cannot be generalized grouped properties.
+ // Treat them as regular properties.
+ // ... unless we're not supposed to handle regular properties. Then ignore them.
+ if (!immediateState)
+ return;
+
+ Q_ASSERT(compilationUnit->stringAt(compilationUnit->objectAt(binding->value.objectIndex)
+ ->inheritedTypeNameIndex).isEmpty());
+
+ const QV4::ResolvedTypeReference *typeReference
+ = compilationUnit->resolvedType(binding->propertyNameIndex);
+ Q_ASSERT(typeReference);
+ QQmlType attachedType = typeReference->type();
+ if (!attachedType.isValid()) {
+ if (QQmlTypeLoader *typeLoader = compilationUnit->engine->typeLoader()) {
+ const QQmlTypeNameCache::Result result
+ = deferredData->context->imports()->query(propertySuffix, typeLoader);
+ if (!result.isValid()) {
+ qmlWarning(q).nospace()
+ << "Unknown name " << propertySuffix << ". The binding is ignored.";
+ return;
+ }
+ attachedType = result.type;
+ }
+ }
+
+ QQmlContext *context = qmlContext(q);
+ QObject *attachedObject = qmlAttachedPropertiesObject(
+ q, attachedType.attachedPropertiesFunction(
+ QQmlEnginePrivate::get(context->engine())));
+ if (!attachedObject) {
+ qmlWarning(q).nospace() <<"Could not create attached properties object '"
+ << attachedType.typeName() << "'";
+ return;
+ }
+
+ initCreator(deferredData, QQmlContextData::get(context), immediateState);
+ immediateState->creator()->populateDeferredInstance(
+ q, deferredData->deferredIdx, binding->value.objectIndex, attachedObject,
+ attachedObject, /*value type property*/ nullptr, binding);
+ return;
+ }
+ Q_FALLTHROUGH();
+ case QV4::CompiledData::Binding::Type_GroupProperty: {
const QString pre = propertyName + u'.';
const QV4::CompiledData::Object *subObj
= compilationUnit->objectAt(binding->value.objectIndex);
@@ -760,13 +836,7 @@ void QQmlBindPrivate::decodeBinding(
QQmlProperty property = QQmlPropertyPrivate::create(
q, propertyName, contextData, QQmlPropertyPrivate::InitFlag::AllowSignal);
if (property.isValid()) {
- if (!immediateState->hasCreator()) {
- immediateState->setCompletePending(true);
- immediateState->initCreator(
- deferredData->context->parent(), deferredData->compilationUnit,
- contextData);
- immediateState->creator()->beginPopulateDeferred(deferredData->context);
- }
+ initCreator(deferredData, contextData, immediateState);
immediateState->creator()->populateDeferredBinding(
property, deferredData->deferredIdx, binding);
} else {
@@ -831,14 +901,14 @@ void QQmlBindPrivate::createDelayedValues()
delayedValues = std::make_unique<QQmlPropertyMap>();
QObject::connect(
delayedValues.get(), &QQmlPropertyMap::valueChanged,
- delayedValues.get(), [this](const QString &delayedName, const QVariant &value) {
+ delayedValues.get(), [this](QString delayedName, const QVariant &value) {
Q_UNUSED(value);
- onDelayedValueChanged(delayedName);
+ onDelayedValueChanged(std::move(delayedName));
}
);
}
-void QQmlBindPrivate::onDelayedValueChanged(const QString &delayedName)
+void QQmlBindPrivate::onDelayedValueChanged(QString delayedName)
{
Q_ASSERT(delayed);
Q_ASSERT(delayedValues);
@@ -849,7 +919,7 @@ void QQmlBindPrivate::onDelayedValueChanged(const QString &delayedName)
else if (pending.contains(delayedName))
return;
- pending.append(delayedName);
+ pending.append(std::move(delayedName));
(*delayedValues)[pendingName].setValue(std::move(pending));
}
@@ -959,6 +1029,7 @@ void QQmlBind::eval()
break;
case QQmlBindEntryKind::V4Value:
if (d->restoreValue) {
+ QQmlAnyBinding::takeFrom(entry.prop); // we don't want to have a binding active
auto propPriv = QQmlPropertyPrivate::get(entry.prop);
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object);
Q_ASSERT(vmemo);
@@ -969,6 +1040,7 @@ void QQmlBind::eval()
break;
case QQmlBindEntryKind::Variant:
if (d->restoreValue) {
+ QQmlAnyBinding::takeFrom(entry.prop); // we don't want to have a binding active
entry.prop.write(entry.previous.variant);
entry.clearPrev();
}