diff options
Diffstat (limited to 'src/qml/types/qqmlbind.cpp')
-rw-r--r-- | src/qml/types/qqmlbind.cpp | 162 |
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(); } |