diff options
Diffstat (limited to 'src/qml/qml/qqmlproperty.cpp')
-rw-r--r-- | src/qml/qml/qqmlproperty.cpp | 970 |
1 files changed, 619 insertions, 351 deletions
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index af5d916c68..7c78fbb984 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1,78 +1,41 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 "qqmlproperty.h" -#include "qqmlproperty_p.h" - -#include "qqml.h" -#include "qqmlbinding_p.h" -#include "qqmlboundsignal_p.h" -#include "qqmlcontext.h" -#include "qqmlcontext_p.h" -#include "qqmlboundsignal_p.h" -#include "qqmlengine.h" -#include "qqmlengine_p.h" -#include "qqmldata_p.h" -#include "qqmlstringconverters_p.h" -#include "qqmllist_p.h" -#include "qqmlvmemetaobject_p.h" -#include "qqmlexpression_p.h" -#include "qqmlvaluetypeproxybinding_p.h" -#include <private/qjsvalue_p.h> -#include <private/qv4functionobject_p.h> -#include <QStringList> -#include <QVector> +#include <private/qjsvalue_p.h> #include <private/qmetaobject_p.h> +#include <private/qproperty_p.h> +#include <private/qqmlboundsignal_p.h> +#include <private/qqmlbuiltinfunctions_p.h> +#include <private/qqmldata_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlirbuilder_p.h> +#include <private/qqmllist_p.h> +#include <private/qqmlproperty_p.h> +#include <private/qqmlsignalnames_p.h> +#include <private/qqmlstringconverters_p.h> +#include <private/qqmlvaluetypeproxybinding_p.h> #include <private/qqmlvaluetypewrapper_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4qobjectwrapper_p.h> + +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlpropertymap.h> + #include <QtCore/qdebug.h> -#include <cmath> -#include <QtQml/QQmlPropertyMap> +#include <QtCore/qsequentialiterable.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvector.h> -Q_DECLARE_METATYPE(QList<int>) -Q_DECLARE_METATYPE(QList<qreal>) -Q_DECLARE_METATYPE(QList<bool>) -Q_DECLARE_METATYPE(QList<QString>) -Q_DECLARE_METATYPE(QList<QUrl>) +#include <cmath> QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(compatResolveUrlsOnAssigment, QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT); + /*! \class QQmlProperty \since 5.0 @@ -117,10 +80,7 @@ qWarning() << "Pixel size should now be 24:" << property.read().toInt(); /*! Create an invalid QQmlProperty. */ -QQmlProperty::QQmlProperty() -: d(nullptr) -{ -} +QQmlProperty::QQmlProperty() = default; /*! \internal */ QQmlProperty::~QQmlProperty() @@ -149,8 +109,10 @@ QQmlProperty::QQmlProperty(QObject *obj) QQmlProperty::QQmlProperty(QObject *obj, QQmlContext *ctxt) : d(new QQmlPropertyPrivate) { - d->context = ctxt?QQmlContextData::get(ctxt):nullptr; - d->engine = ctxt?ctxt->engine():nullptr; + if (ctxt) { + d->context = QQmlContextData::get(ctxt); + d->engine = ctxt->engine(); + } d->initDefault(obj); } @@ -201,12 +163,15 @@ QQmlProperty::QQmlProperty(QObject *obj, const QString &name) QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt) : d(new QQmlPropertyPrivate) { - d->context = ctxt?QQmlContextData::get(ctxt):nullptr; - d->engine = ctxt?ctxt->engine():nullptr; + if (ctxt) { + d->context = QQmlContextData::get(ctxt); + d->engine = ctxt->engine(); + } + d->initProperty(obj, name); if (!isValid()) { d->object = nullptr; - d->context = nullptr; + d->context.reset(); d->engine = nullptr; } } @@ -223,28 +188,34 @@ QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine d->initProperty(obj, name); if (!isValid()) { d->object = nullptr; - d->context = nullptr; + d->context.reset(); d->engine = nullptr; } } QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propertyName, - const QQmlRefPointer<QQmlContextData> &context) + const QQmlRefPointer<QQmlContextData> &context, + QQmlPropertyPrivate::InitFlags flags) { QQmlProperty result; auto d = new QQmlPropertyPrivate; result.d = d; d->context = context; d->engine = context ? context->engine() : nullptr; - d->initProperty(target, propertyName); + d->initProperty(target, propertyName, flags); if (!result.isValid()) { d->object = nullptr; - d->context = nullptr; + d->context.reset(); d->engine = nullptr; } return result; } +bool QQmlPropertyPrivate::resolveUrlsOnAssignment() +{ + return ::compatResolveUrlsOnAssigment(); +} + QQmlRefPointer<QQmlContextData> QQmlPropertyPrivate::effectiveContext() const { if (context) @@ -255,10 +226,10 @@ QQmlRefPointer<QQmlContextData> QQmlPropertyPrivate::effectiveContext() const return nullptr; } -void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) +// ### Qt7: Do not accept the "onFoo" syntax for signals anymore, and change the flags accordingly. +void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, + QQmlPropertyPrivate::InitFlags flags) { - if (!obj) return; - QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context ? context->imports() : nullptr; QObject *currentObject = obj; @@ -270,33 +241,40 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) if (path.isEmpty()) return; // Everything up to the last property must be an "object type" property - for (int ii = 0; ii < path.count() - 1; ++ii) { + for (int ii = 0; ii < path.size() - 1; ++ii) { const QStringView &pathName = path.at(ii); // Types must begin with an uppercase letter (see checkRegistration() // in qqmlmetatype.cpp for the enforcement of this). if (typeNameCache && !pathName.isEmpty() && pathName.at(0).isUpper()) { - QQmlTypeNameCache::Result r = typeNameCache->query(pathName); + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); + QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(enginePrivate); + QQmlTypeNameCache::Result r = typeNameCache->query(pathName, typeLoader); if (r.isValid()) { if (r.type.isValid()) { - QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); if (!func) return; // Not an attachable type currentObject = qmlAttachedPropertiesObject(currentObject, func); if (!currentObject) return; // Something is broken with the attachable type } else if (r.importNamespace) { - if ((ii + 1) == path.count()) return; // No type following the namespace + if (++ii == path.size()) + return; // No type following the namespace + + // TODO: Do we really _not_ want to query the namespaced types here? + r = typeNameCache->query<QQmlTypeNameCache::QueryNamespaced::No>( + path.at(ii), r.importNamespace, typeLoader); - ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace); - if (!r.type.isValid()) return; // Invalid type in namespace + if (!r.type.isValid()) + return; // Invalid type in namespace - QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); - if (!func) return; // Not an attachable type + if (!func) + return; // Not an attachable type currentObject = qmlAttachedPropertiesObject(currentObject, func); - if (!currentObject) return; // Something is broken with the attachable type + if (!currentObject) + return; // Something is broken with the attachable type } else if (r.scriptIndex != -1) { return; // Not a type @@ -309,16 +287,35 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) } QQmlPropertyData local; - QQmlPropertyData *property = - QQmlPropertyCache::property(engine, currentObject, pathName, context, &local); + const QQmlPropertyData *property = currentObject + ? QQmlPropertyCache::property(currentObject, pathName, context, &local) + : nullptr; + + if (!property) { + // Not a property; Might be an ID + // You can't look up an ID on a non-null object, though. + if (currentObject || !(flags & InitFlag::AllowId)) + return; + + for (auto idContext = context; idContext; idContext = idContext->parent()) { + const int objectId = idContext->propertyIndex(pathName.toString()); + if (objectId != -1 && objectId < idContext->numIdValues()) { + currentObject = context->idValue(objectId); + break; + } + } - if (!property) return; // Not a property - if (property->isFunction()) + if (!currentObject) + return; + + continue; + } else if (property->isFunction()) { return; // Not an object property + } - if (ii == (path.count() - 2) && QQmlMetaType::isValueType(property->propType())) { + if (ii == (path.size() - 2) && QQmlMetaType::isValueType(property->propType())) { // We're now at a value type property - const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForMetaType(property->propType()); + const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(property->propType()); if (!valueTypeMetaObject) return; // Not a value type int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData()); @@ -352,68 +349,119 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) } terminal = path.last(); + } else if (!currentObject) { + return; } - if (terminal.size() >= 3 && terminal.at(0) == u'o' && terminal.at(1) == u'n' - && (terminal.at(2).isUpper() || terminal.at(2) == u'_')) { + auto findSignalInMetaObject = [&](const QByteArray &signalName) { + const QMetaMethod method = findSignalByName(currentObject->metaObject(), signalName); + if (!method.isValid()) + return false; - QString signalName = terminal.mid(2).toString(); - int firstNon_; - int length = signalName.length(); - for (firstNon_ = 0; firstNon_ < length; ++firstNon_) - if (signalName.at(firstNon_) != u'_') - break; - signalName[firstNon_] = signalName.at(firstNon_).toLower(); + object = currentObject; + core.load(method); + return true; + }; - // XXX - this code treats methods as signals + QQmlData *ddata = QQmlData::get(currentObject, false); + auto findChangeSignal = [&](QStringView signalName) { + if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(signalName)) { + const QQmlPropertyData *d = + ddata->propertyCache->property(*propName, currentObject, context); + while (d && d->isFunction()) + d = ddata->propertyCache->overrideData(d); - QQmlData *ddata = QQmlData::get(currentObject, false); - if (ddata && ddata->propertyCache) { + if (d && d->notifyIndex() != -1) { + object = currentObject; + core = *ddata->propertyCache->signal(d->notifyIndex()); + return true; + } + } + return false; + }; + const auto findSignal = [&](const QString &signalName) { + if (ddata && ddata->propertyCache) { // Try method - QQmlPropertyData *d = ddata->propertyCache->property(signalName, currentObject, context); + const QQmlPropertyData *d + = ddata->propertyCache->property(signalName, currentObject, context); + + // ### Qt7: This code treats methods as signals. It should use d->isSignal(). + // That would be a change in behavior, though. Right now you can construct a + // QQmlProperty from such a thing. while (d && !d->isFunction()) d = ddata->propertyCache->overrideData(d); if (d) { object = currentObject; core = *d; - return; + return true; } - // Try property - if (signalName.endsWith(QLatin1String("Changed"))) { - const QStringView propName = QStringView{signalName}.mid(0, signalName.length() - 7); - QQmlPropertyData *d = ddata->propertyCache->property(propName, currentObject, context); - while (d && d->isFunction()) - d = ddata->propertyCache->overrideData(d); + return findChangeSignal(signalName); + } + + return findSignalInMetaObject(signalName.toUtf8()); + }; - if (d && d->notifyIndex() != -1) { - object = currentObject; - core = *ddata->propertyCache->signal(d->notifyIndex()); - return; - } + auto signalName = QQmlSignalNames::handlerNameToSignalName(terminal); + if (signalName) { + if (findSignal(*signalName)) + return; + } else { + signalName = QQmlSignalNames::badHandlerNameToSignalName(terminal); + if (signalName) { + if (findSignal(*signalName)) { + qWarning() + << terminal + << "is not a properly capitalized signal handler name." + << QQmlSignalNames::signalNameToHandlerName(*signalName) + << "would be correct."; + return; } + } + } - } else { - QMetaMethod method = findSignalByName(currentObject->metaObject(), - signalName.toLatin1()); - if (method.isValid()) { + if (ddata && ddata->propertyCache) { + const QQmlPropertyData *property = ddata->propertyCache->property( + terminal, currentObject, context); + + // Technically, we might find an override that is not a function. + while (property && !property->isSignal()) { + if (!property->isFunction()) { object = currentObject; - core.load(method); + core = *property; + nameCache = terminal.toString(); return; } + property = ddata->propertyCache->overrideData(property); } - } - // Property - QQmlPropertyData local; - QQmlPropertyData *property = - QQmlPropertyCache::property(engine, currentObject, terminal, context, &local); - if (property && !property->isFunction()) { - object = currentObject; - core = *property; - nameCache = terminal.toString(); + if (!(flags & InitFlag::AllowSignal)) + return; + + if (property) { + Q_ASSERT(property->isSignal()); + object = currentObject; + core = *property; + return; + } + + // At last: Try the change signal. + findChangeSignal(terminal); + } else { + // We might still find the property in the metaobject, even without property cache. + const QByteArray propertyName = terminal.toUtf8(); + const QMetaProperty prop = findPropertyByName(currentObject->metaObject(), propertyName); + + if (prop.isValid()) { + object = currentObject; + core.load(prop); + return; + } + + if (flags & InitFlag::AllowSignal) + findSignalInMetaObject(terminal.toUtf8()); } } @@ -501,7 +549,7 @@ const char *QQmlProperty::propertyTypeName() const if (!d) return nullptr; if (d->isValueType()) { - const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForMetaType(d->core.propType()); + const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(d->core.propType()); Q_ASSERT(valueTypeMetaObject); return valueTypeMetaObject->property(d->valueTypeData.coreIndex()).typeName(); } else if (d->object && type() & Property && d->core.isValid()) { @@ -611,12 +659,8 @@ QObject *QQmlProperty::object() const */ QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other) { - if (d) - d->release(); - d = other.d; - if (d) - d->addref(); - + QQmlProperty copied(other); + qSwap(d, copied.d); return *this; } @@ -699,18 +743,16 @@ QString QQmlProperty::name() const if (!d) return QString(); if (d->nameCache.isEmpty()) { - // ### if (!d->object) { } else if (d->isValueType()) { - const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForMetaType(d->core.propType()); + const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(d->core.propType()); Q_ASSERT(valueTypeMetaObject); const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex()).name(); d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName); } else if (type() & SignalProperty) { - QString name = QLatin1String("on") + d->core.name(d->object); - name[2] = name.at(2).toUpper(); - d->nameCache = name; + // ### Qt7: Return the original signal name here. Do not prepend "on" + d->nameCache = QQmlSignalNames::signalNameToHandlerName(d->core.name(d->object)); } else { d->nameCache = d->core.name(d->object); } @@ -807,7 +849,7 @@ static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPrope if (!oldBinding) return; - if (valueTypeIndex != -1 && oldBinding->isValueTypeProxy()) + if (valueTypeIndex != -1 && oldBinding->kind() == QQmlAbstractBinding::ValueTypeProxy) oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index); if (!oldBinding) @@ -862,9 +904,8 @@ QQmlPropertyPrivate::binding(QObject *object, QQmlPropertyIndex index) binding = binding->nextBinding(); if (binding && valueTypeIndex != -1) { - if (binding->isValueTypeProxy()) { + if (binding->kind() == QQmlAbstractBinding::ValueTypeProxy) binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); - } } return binding; @@ -879,7 +920,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bin int coreIndex = bindingIndex.coreIndex(); int valueTypeIndex = bindingIndex.valueTypeIndex(); - QQmlPropertyData *propertyData = + const QQmlPropertyData *propertyData = data->propertyCache?data->propertyCache->property(coreIndex):nullptr; if (propertyData && propertyData->isAlias()) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); @@ -927,7 +968,7 @@ void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags int coreIndex = index.coreIndex(); QQmlData *data = QQmlData::get(object, true); if (data->propertyCache) { - QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); Q_ASSERT(propertyData); } #endif @@ -1087,7 +1128,7 @@ QVariant QQmlPropertyPrivate::readValueProperty() { auto doRead = [&](QQmlGadgetPtrWrapper *wrapper) { wrapper->read(object, core.coreIndex()); - return wrapper->property(valueTypeData.coreIndex()).read(wrapper); + return wrapper->readOnGadget(wrapper->property(valueTypeData.coreIndex())); }; if (isValueType()) { @@ -1102,7 +1143,7 @@ QVariant QQmlPropertyPrivate::readValueProperty() QQmlListProperty<QObject> prop; core.readProperty(object, &prop); - return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType().id(), engine)); + return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType())); } else if (core.isQObject()) { @@ -1133,32 +1174,38 @@ QVariant QQmlPropertyPrivate::readValueProperty() } // helper function to allow assignment / binding to QList<QUrl> properties. -QVariant QQmlPropertyPrivate::urlSequence(const QVariant &value) +QList<QUrl> QQmlPropertyPrivate::urlSequence(const QVariant &value) { + if (value.metaType() == QMetaType::fromType<QList<QUrl>>()) + return value.value<QList<QUrl> >(); + QList<QUrl> urls; - if (value.userType() == qMetaTypeId<QUrl>()) { + if (value.metaType() == QMetaType::fromType<QUrl>()) { urls.append(value.toUrl()); - } else if (value.userType() == qMetaTypeId<QString>()) { + } else if (value.metaType() == QMetaType::fromType<QString>()) { urls.append(QUrl(value.toString())); - } else if (value.userType() == qMetaTypeId<QByteArray>()) { + } else if (value.metaType() == QMetaType::fromType<QByteArray>()) { urls.append(QUrl(QString::fromUtf8(value.toByteArray()))); - } else if (value.userType() == qMetaTypeId<QList<QUrl> >()) { - urls = value.value<QList<QUrl> >(); - } else if (value.userType() == qMetaTypeId<QStringList>()) { + } else if (value.metaType() == QMetaType::fromType<QStringList>()) { QStringList urlStrings = value.value<QStringList>(); const int urlStringsSize = urlStrings.size(); urls.reserve(urlStringsSize); for (int i = 0; i < urlStringsSize; ++i) urls.append(QUrl(urlStrings.at(i))); - } else if (value.userType() == qMetaTypeId<QList<QString> >()) { - QList<QString> urlStrings = value.value<QList<QString> >(); - const int urlStringsSize = urlStrings.size(); - urls.reserve(urlStringsSize); - for (int i = 0; i < urlStringsSize; ++i) - urls.append(QUrl(urlStrings.at(i))); } // note: QList<QByteArray> is not currently supported. + return urls; +} + +// ### Qt7: Get rid of this +QList<QUrl> QQmlPropertyPrivate::urlSequence( + const QVariant &value, const QQmlRefPointer<QQmlContextData> &ctxt) +{ + QList<QUrl> urls = urlSequence(value); - return QVariant::fromValue<QList<QUrl> >(urls); + for (auto urlIt = urls.begin(); urlIt != urls.end(); ++urlIt) + *urlIt = ctxt->resolvedUrl(*urlIt); + + return urls; } //writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC! @@ -1168,13 +1215,9 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, return false; QVariant v = value; - if (prop.isEnumType()) { + if (prop.isEnumType() && v.metaType() != prop.metaType()) { QMetaEnum menum = prop.enumerator(); - if (v.userType() == QMetaType::QString -#ifdef QT3_SUPPORT - || v.userType() == QVariant::CString -#endif - ) { + if (v.userType() == QMetaType::QString) { bool ok; if (prop.isFlagType()) v = QVariant(menum.keysToValue(value.toByteArray(), &ok)); @@ -1182,14 +1225,9 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, v = QVariant(menum.keyToValue(value.toByteArray(), &ok)); if (!ok) return false; - } else if (v.userType() != QMetaType::Int && v.userType() != QMetaType::UInt) { - int enumMetaTypeId = QMetaType::fromName( - QByteArray(menum.scope() + QByteArray("::") + menum.name())).id(); - if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData()) - return false; - v = QVariant(*reinterpret_cast<const int *>(v.constData())); } - v.convert(QMetaType(QMetaType::Int)); + if (!v.convert(prop.metaType())) // ### TODO: underlyingType might be faster? + return false; } // the status variable is changed by qt_metacall to indicate what it did @@ -1208,41 +1246,228 @@ bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlProperty return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags); } -bool -QQmlPropertyPrivate::writeValueProperty(QObject *object, - const QQmlPropertyData &core, - const QQmlPropertyData &valueTypeData, - const QVariant &value, - const QQmlRefPointer<QQmlContextData> &context, - QQmlPropertyData::WriteFlags flags) +static void removeValuePropertyBinding( + QObject *object, const QQmlPropertyData &core, + const QQmlPropertyData &valueTypeData, QQmlPropertyData::WriteFlags flags) { // Remove any existing bindings on this property - if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) - removeBinding(object, encodedIndex(core, valueTypeData)); - - bool rv = false; - if (valueTypeData.isValid()) { - auto doWrite = [&](QQmlGadgetPtrWrapper *wrapper) { - wrapper->read(object, core.coreIndex()); - rv = write(wrapper, valueTypeData, value, context, flags); - wrapper->write(object, core.coreIndex(), flags); - }; - - QQmlGadgetPtrWrapper *wrapper = context - ? QQmlGadgetPtrWrapper::instance(context->engine(), core.propType()) - : nullptr; - if (wrapper) { - doWrite(wrapper); - } else if (QQmlValueType *valueType = QQmlMetaType::valueType(core.propType())) { - QQmlGadgetPtrWrapper wrapper(valueType, nullptr); - doWrite(&wrapper); + if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) { + QQmlPropertyPrivate::removeBinding( + object, QQmlPropertyPrivate::encodedIndex(core, valueTypeData)); + } +} + +template<typename Op> +bool changePropertyAndWriteBack( + QObject *object, int coreIndex, QQmlGadgetPtrWrapper *wrapper, + QQmlPropertyData::WriteFlags flags, int internalIndex, Op op) +{ + wrapper->read(object, coreIndex); + const bool rv = op(wrapper); + wrapper->write(object, coreIndex, flags, internalIndex); + return rv; +} + +template<typename Op> +bool changeThroughGadgetPtrWrapper( + QObject *object, const QQmlPropertyData &core, + const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags, + int internalIndex, Op op) +{ + if (QQmlGadgetPtrWrapper *wrapper = context + ? QQmlGadgetPtrWrapper::instance(context->engine(), core.propType()) + : nullptr) { + return changePropertyAndWriteBack( + object, core.coreIndex(), wrapper, flags, internalIndex, op); + } + + if (QQmlValueType *valueType = QQmlMetaType::valueType(core.propType())) { + QQmlGadgetPtrWrapper wrapper(valueType, nullptr); + return changePropertyAndWriteBack( + object, core.coreIndex(), &wrapper, flags, internalIndex, op); + } + + return false; +} + +bool QQmlPropertyPrivate::writeValueProperty( + QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, + const QVariant &value, const QQmlRefPointer<QQmlContextData> &context, + QQmlPropertyData::WriteFlags flags) +{ + removeValuePropertyBinding(object, core, valueTypeData, flags); + + if (!valueTypeData.isValid()) + return write(object, core, value, context, flags); + + return changeThroughGadgetPtrWrapper( + object, core, context, flags | QQmlPropertyData::HasInternalIndex, + valueTypeData.coreIndex(), [&](QQmlGadgetPtrWrapper *wrapper) { + return write(wrapper, valueTypeData, value, context, flags); + }); +} + +bool QQmlPropertyPrivate::resetValueProperty( + QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, + const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags) +{ + removeValuePropertyBinding(object, core, valueTypeData, flags); + + if (!valueTypeData.isValid()) + return reset(object, core, flags); + + return changeThroughGadgetPtrWrapper( + object, core, context, flags | QQmlPropertyData::HasInternalIndex, + valueTypeData.coreIndex(), [&](QQmlGadgetPtrWrapper *wrapper) { + return reset(wrapper, valueTypeData, flags); + }); +} + +// We need to prevent new-style bindings from being removed. +struct BindingFixer +{ + Q_DISABLE_COPY_MOVE(BindingFixer); + + BindingFixer(QObject *object, const QQmlPropertyData &property, + QQmlPropertyData::WriteFlags flags) + { + if (!property.isBindable() || !(flags & QQmlPropertyData::DontRemoveBinding)) + return; + + QUntypedBindable bindable; + void *argv[] = {&bindable}; + QMetaObject::metacall(object, QMetaObject::BindableProperty, property.coreIndex(), argv); + untypedBinding = bindable.binding(); + if (auto priv = QPropertyBindingPrivate::get(untypedBinding)) + priv->setSticky(true); + } + + ~BindingFixer() + { + if (untypedBinding.isNull()) + return; + auto priv = QPropertyBindingPrivate::get(untypedBinding); + priv->setSticky(false); + } + +private: + QUntypedPropertyBinding untypedBinding; +}; + +struct ConvertAndAssignResult { + bool couldConvert = false; + bool couldWrite = false; + + operator bool() const { return couldConvert; } +}; + +static ConvertAndAssignResult tryConvertAndAssign( + QObject *object, const QQmlPropertyData &property, const QVariant &value, + QQmlPropertyData::WriteFlags flags, QMetaType propertyMetaType, QMetaType variantMetaType, + bool isUrl) { + + if (isUrl + || variantMetaType == QMetaType::fromType<QString>() + || propertyMetaType == QMetaType::fromType<QList<QUrl>>() + || property.isQList()) { + return {false, false}; + } + + if (variantMetaType == QMetaType::fromType<QJSValue>()) { + // Handle Qt.binding bindings here to avoid mistaken conversion below + const QJSValue &jsValue = get<QJSValue>(value); + const QV4::FunctionObject *f + = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue); + if (f && f->isBinding()) { + QV4::QObjectWrapper::setProperty( + f->engine(), object, &property, f->asReturnedValue()); + return {true, true}; } + } - } else { - rv = write(object, core, value, context, flags); + // common cases: + switch (propertyMetaType.id()) { + case QMetaType::Bool: + if (value.canConvert(propertyMetaType)) { + bool b = value.toBool(); + return {true, property.writeProperty(object, &b, flags)}; + } + return {false, false}; + case QMetaType::Int: { + bool ok = false; + int i = value.toInt(&ok); + return {ok, ok && property.writeProperty(object, &i, flags)}; + } + case QMetaType::UInt: { + bool ok = false; + uint u = value.toUInt(&ok); + return {ok, ok && property.writeProperty(object, &u, flags)}; + } + case QMetaType::Double: { + bool ok = false; + double d = value.toDouble(&ok); + return {ok, ok && property.writeProperty(object, &d, flags)}; + } + case QMetaType::Float: { + bool ok = false; + float f = value.toFloat(&ok); + return {ok, ok && property.writeProperty(object, &f, flags)}; + } + case QMetaType::QString: + if (value.canConvert(propertyMetaType)) { + QString s = value.toString(); + return {true, property.writeProperty(object, &s, flags)}; + } + return {false, false}; + case QMetaType::QVariantMap: + if (value.canConvert(propertyMetaType)) { + QVariantMap m = value.toMap(); + return {true, property.writeProperty(object, &m, flags)}; + } + return {false, false}; + default: { + break; + } } - return rv; + QVariant converted = QQmlValueTypeProvider::createValueType(value, propertyMetaType); + if (!converted.isValid()) { + converted = QVariant(propertyMetaType); + if (!QMetaType::convert(value.metaType(), value.constData(), + propertyMetaType, converted.data())) { + return {false, false}; + } + } + return {true, property.writeProperty(object, converted.data(), flags)}; +}; + +template<typename Op> +bool iterateQObjectContainer(QMetaType metaType, const void *data, Op op) +{ + QSequentialIterable iterable; + if (!QMetaType::convert(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable)) + return false; + + const QMetaSequence metaSequence = iterable.metaContainer(); + + if (!metaSequence.hasConstIterator() + || !metaSequence.canGetValueAtConstIterator() + || !iterable.valueMetaType().flags().testFlag(QMetaType::PointerToQObject)) { + return false; + } + + const void *container = iterable.constIterable(); + void *it = metaSequence.constBegin(container); + const void *end = metaSequence.constEnd(container); + QObject *o = nullptr; + while (!metaSequence.compareConstIterator(it, end)) { + metaSequence.valueAtConstIterator(it, &o); + op(o); + metaSequence.advanceConstIterator(it, 1); + } + metaSequence.destroyConstIterator(it); + metaSequence.destroyConstIterator(end); + return true; } bool QQmlPropertyPrivate::write( @@ -1250,14 +1475,15 @@ bool QQmlPropertyPrivate::write( const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags) { const QMetaType propertyMetaType = property.propType(); - const int propertyType = propertyMetaType.id(); - const int variantType = value.userType(); + const QMetaType variantMetaType = value.metaType(); + + const BindingFixer bindingFixer(object, property, flags); if (property.isEnum()) { QMetaProperty prop = object->metaObject()->property(property.coreIndex()); QVariant v = value; // Enum values come through the script engine as doubles - if (variantType == QMetaType::Double) { + if (variantMetaType == QMetaType::fromType<double>()) { double integral; double fractional = std::modf(value.toDouble(), &integral); if (qFuzzyIsNull(fractional)) @@ -1267,25 +1493,28 @@ bool QQmlPropertyPrivate::write( } QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context); - const bool isUrl = propertyType == QMetaType::QUrl; // handled separately + const bool isUrl = propertyMetaType == QMetaType::fromType<QUrl>(); // handled separately // The cases below are in approximate order of likelyhood: - if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) { + if (propertyMetaType == variantMetaType && !isUrl + && propertyMetaType != QMetaType::fromType<QList<QUrl>>() && !property.isQList()) { return property.writeProperty(object, const_cast<void *>(value.constData()), flags); } else if (property.isQObject()) { QVariant val = value; - int varType = variantType; - if (variantType == QMetaType::Nullptr) { + QMetaType varType; + if (variantMetaType == QMetaType::fromType<std::nullptr_t>()) { // This reflects the fact that you can assign a nullptr to a QObject pointer // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject - varType = QMetaType::QObjectStar; - val = QVariant(QMetaType::fromType<QObject *>(), nullptr); + varType = QMetaType::fromType<QObject*>(); + val = QVariant(varType, nullptr); + } else { + varType = variantMetaType; } - QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType); - if (valMo.isNull()) + QQmlMetaObject valMo = rawMetaObjectForType(varType); + if (valMo.isNull() || !varType.flags().testFlag(QMetaType::PointerToQObject)) return false; QObject *o = *static_cast<QObject *const *>(val.constData()); - QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType); + QQmlMetaObject propMo = rawMetaObjectForType(propertyMetaType); if (o) valMo = o; @@ -1300,166 +1529,189 @@ bool QQmlPropertyPrivate::write( } else { return false; } - } else if (value.canConvert(propertyMetaType) - && !isUrl && variantType != QMetaType::QString - && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) { - // common cases: - switch (propertyType) { - case QMetaType::Bool: { - bool b = value.toBool(); - return property.writeProperty(object, &b, flags); - } - case QMetaType::Int: { - int i = value.toInt(); - return property.writeProperty(object, &i, flags); - } - case QMetaType::Double: { - double d = value.toDouble(); - return property.writeProperty(object, &d, flags); - } - case QMetaType::Float: { - float f = value.toFloat(); - return property.writeProperty(object, &f, flags); - } - case QMetaType::QString: { - QString s = value.toString(); - return property.writeProperty(object, &s, flags); - } - default: { // "fallback": - QVariant v = value; - v.convert(propertyMetaType); - return property.writeProperty(object, const_cast<void *>(v.constData()), flags); - } - } - } else if (propertyType == qMetaTypeId<QVariant>()) { + } else if (ConvertAndAssignResult result = tryConvertAndAssign( + object, property, value, flags, propertyMetaType, variantMetaType, isUrl)) { + return result.couldWrite; + } else if (propertyMetaType == QMetaType::fromType<QVariant>()) { return property.writeProperty(object, const_cast<QVariant *>(&value), flags); } else if (isUrl) { QUrl u; - if (variantType == QMetaType::QUrl) + if (variantMetaType == QMetaType::fromType<QUrl>()) { u = value.toUrl(); - else if (variantType == QMetaType::QByteArray) + if (compatResolveUrlsOnAssigment() && context && u.isRelative() && !u.isEmpty()) + u = context->resolvedUrl(u); + } + else if (variantMetaType == QMetaType::fromType<QByteArray>()) u = QUrl(QString::fromUtf8(value.toByteArray())); - else if (variantType == QMetaType::QString) + else if (variantMetaType == QMetaType::fromType<QString>()) u = QUrl(value.toString()); else return false; return property.writeProperty(object, &u, flags); - } else if (propertyType == qMetaTypeId<QList<QUrl>>()) { - QList<QUrl> urlSeq = urlSequence(value).value<QList<QUrl>>(); + } else if (propertyMetaType == QMetaType::fromType<QList<QUrl>>()) { + QList<QUrl> urlSeq = compatResolveUrlsOnAssigment() + ? urlSequence(value, context) + : urlSequence(value); return property.writeProperty(object, &urlSeq, flags); } else if (property.isQList()) { - QQmlMetaObject listType; - - if (enginePriv) { - listType = enginePriv->rawMetaObjectForType(QQmlMetaType::listType(propertyType)); - } else { - QQmlType type = QQmlMetaType::qmlType(QQmlMetaType::listType(propertyType)); - if (!type.isValid()) + if (propertyMetaType.flags() & QMetaType::IsQmlList) { + QMetaType listValueType = QQmlMetaType::listValueType(propertyMetaType); + QQmlMetaObject valueMetaObject = QQmlMetaType::rawMetaObjectForType(listValueType); + if (valueMetaObject.isNull()) return false; - listType = type.baseMetaObject(); - } - if (listType.isNull()) - return false; - QQmlListProperty<void> prop; - property.readProperty(object, &prop); + QQmlListProperty<QObject> prop; + property.readProperty(object, &prop); - if (!prop.clear) - return false; + if (!prop.clear || !prop.append) + return false; - prop.clear(&prop); + const bool useNonsignalingListOps = prop.clear == &QQmlVMEMetaObject::list_clear + && prop.append == &QQmlVMEMetaObject::list_append; - if (variantType == qMetaTypeId<QQmlListReference>()) { - QQmlListReference qdlr = value.value<QQmlListReference>(); + auto propClear = + useNonsignalingListOps ? &QQmlVMEMetaObject::list_clear_nosignal : prop.clear; + auto propAppend = + useNonsignalingListOps ? &QQmlVMEMetaObject::list_append_nosignal : prop.append; - for (qsizetype ii = 0; ii < qdlr.count(); ++ii) { - QObject *o = qdlr.at(ii); - if (o && !QQmlMetaObject::canConvert(o, listType)) - o = nullptr; - prop.append(&prop, o); - } - } else if (variantType == qMetaTypeId<QList<QObject *> >()) { - const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value); + propClear(&prop); - for (qsizetype ii = 0; ii < list.count(); ++ii) { - QObject *o = list.at(ii); - if (o && !QQmlMetaObject::canConvert(o, listType)) + const auto doAppend = [&](QObject *o) { + if (o && !QQmlMetaObject::canConvert(o, valueMetaObject)) o = nullptr; - prop.append(&prop, o); + propAppend(&prop, o); + }; + + if (variantMetaType == QMetaType::fromType<QQmlListReference>()) { + QQmlListReference qdlr = value.value<QQmlListReference>(); + for (qsizetype ii = 0; ii < qdlr.count(); ++ii) + doAppend(qdlr.at(ii)); + } else if (variantMetaType == QMetaType::fromType<QList<QObject *>>()) { + const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value); + for (qsizetype ii = 0; ii < list.size(); ++ii) + doAppend(list.at(ii)); + } else if (variantMetaType == QMetaType::fromType<QList<QVariant>>()) { + const QList<QVariant> &list + = *static_cast<const QList<QVariant> *>(value.constData()); + for (const QVariant &entry : list) + doAppend(QQmlMetaType::toQObject(entry)); + } else if (!iterateQObjectContainer(variantMetaType, value.data(), doAppend)) { + doAppend(QQmlMetaType::toQObject(value)); + } + if (useNonsignalingListOps) { + Q_ASSERT(QQmlVMEMetaObject::get(object)); + QQmlVMEResolvedList(&prop).activateSignal(); } + } else if (variantMetaType == propertyMetaType) { + QVariant v = value; + property.writeProperty(object, v.data(), flags); } else { - QObject *o = QQmlMetaType::toQObject(value); - if (o && !QQmlMetaObject::canConvert(o, listType)) - o = nullptr; - prop.append(&prop, o); + QVariant list(propertyMetaType); + const QQmlType type = QQmlMetaType::qmlType(propertyMetaType); + const QMetaSequence sequence = type.listMetaSequence(); + if (sequence.canAddValue()) + sequence.addValue(list.data(), value.data()); + property.writeProperty(object, list.data(), flags); } + } else if (enginePriv && propertyMetaType == QMetaType::fromType<QJSValue>()) { + // We can convert everything into a QJSValue if we have an engine. + QJSValue jsValue = QJSValuePrivate::fromReturnedValue( + enginePriv->v4engine()->metaTypeToJS(variantMetaType, value.constData())); + return property.writeProperty(object, &jsValue, flags); } else { - Q_ASSERT(variantType != propertyType); + Q_ASSERT(variantMetaType != propertyMetaType); bool ok = false; QVariant v; - if (variantType == QMetaType::QString) - v = QQmlStringConverters::variantFromString(value.toString(), propertyType, &ok); + if (variantMetaType == QMetaType::fromType<QString>()) + v = QQmlStringConverters::variantFromString(value.toString(), propertyMetaType, &ok); if (!ok) { v = value; if (v.convert(propertyMetaType)) { ok = true; - } else if (static_cast<uint>(propertyType) >= QMetaType::User && - variantType == QMetaType::QString) { - QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType); - if (con) { - v = con(value.toString()); - if (v.userType() == propertyType) - ok = true; - } } } if (!ok) { // the only other options are that they are assigning a single value - // to a sequence type property (eg, an int to a QList<int> property). - // or that we encountered an interface type + // or a QVariantList to a sequence type property (eg, an int to a + // QList<int> property) or that we encountered an interface type. // Note that we've already handled single-value assignment to QList<QUrl> properties. - if (variantType == QMetaType::Int && propertyType == qMetaTypeId<QList<int> >()) { - QList<int> list; - list << value.toInt(); - v = QVariant::fromValue<QList<int> >(list); - ok = true; - } else if ((variantType == QMetaType::Double || variantType == QMetaType::Int) - && (propertyType == qMetaTypeId<QList<qreal> >())) { - QList<qreal> list; - list << value.toReal(); - v = QVariant::fromValue<QList<qreal> >(list); - ok = true; - } else if (variantType == QMetaType::Bool && propertyType == qMetaTypeId<QList<bool> >()) { - QList<bool> list; - list << value.toBool(); - v = QVariant::fromValue<QList<bool> >(list); - ok = true; - } else if (variantType == QMetaType::QString && propertyType == qMetaTypeId<QList<QString> >()) { - QList<QString> list; - list << value.toString(); - v = QVariant::fromValue<QList<QString> >(list); - ok = true; - } else if (variantType == QMetaType::QString && propertyType == qMetaTypeId<QStringList>()) { - QStringList list; - list << value.toString(); - v = QVariant::fromValue<QStringList>(list); - ok = true; + QSequentialIterable iterable; + v = QVariant(propertyMetaType); + if (QMetaType::view( + propertyMetaType, v.data(), + QMetaType::fromType<QSequentialIterable>(), + &iterable)) { + const QMetaSequence propertyMetaSequence = iterable.metaContainer(); + if (propertyMetaSequence.canAddValueAtEnd()) { + const QMetaType elementMetaType = iterable.valueMetaType(); + void *propertyContainer = iterable.mutableIterable(); + + if (variantMetaType == elementMetaType) { + propertyMetaSequence.addValueAtEnd(propertyContainer, value.constData()); + ok = true; + } else if (variantMetaType == QMetaType::fromType<QVariantList>()) { + const QVariantList list = value.value<QVariantList>(); + for (const QVariant &valueElement : list) { + if (valueElement.metaType() == elementMetaType) { + propertyMetaSequence.addValueAtEnd( + propertyContainer, valueElement.constData()); + } else { + QVariant converted(elementMetaType); + QMetaType::convert( + valueElement.metaType(), valueElement.constData(), + elementMetaType, converted.data()); + propertyMetaSequence.addValueAtEnd( + propertyContainer, converted.constData()); + } + } + ok = true; + } else if (elementMetaType.flags().testFlag(QMetaType::PointerToQObject)) { + const QMetaObject *elementMetaObject = elementMetaType.metaObject(); + Q_ASSERT(elementMetaObject); + + const auto doAppend = [&](QObject *o) { + QObject *casted = elementMetaObject->cast(o); + propertyMetaSequence.addValueAtEnd(propertyContainer, &casted); + }; + + if (variantMetaType.flags().testFlag(QMetaType::PointerToQObject)) { + doAppend(*static_cast<QObject *const *>(value.data())); + ok = true; + } else if (variantMetaType == QMetaType::fromType<QQmlListReference>()) { + const QQmlListReference *reference + = static_cast<const QQmlListReference *>(value.constData()); + Q_ASSERT(elementMetaObject); + for (int i = 0, end = reference->size(); i < end; ++i) + doAppend(reference->at(i)); + ok = true; + } else if (!iterateQObjectContainer( + variantMetaType, value.data(), doAppend)) { + doAppend(QQmlMetaType::toQObject(value)); + } + } else { + QVariant converted = value; + if (converted.convert(elementMetaType)) { + propertyMetaSequence.addValueAtEnd(propertyContainer, converted.constData()); + ok = true; + } + } + } } } - if (!ok && QQmlMetaType::isInterface(propertyType)) { + if (!ok && QQmlMetaType::isInterface(propertyMetaType)) { auto valueAsQObject = qvariant_cast<QObject *>(value); - if (void *interface = valueAsQObject - ? valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyType)) + if (void *iface = valueAsQObject + ? valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyMetaType)) : nullptr; - interface) { + iface) { // this case can occur when object has an interface type // and the variant contains a type implementing the interface - return property.writeProperty(object, &interface, flags); + return property.writeProperty(object, &iface, flags); } } @@ -1473,17 +1725,22 @@ bool QQmlPropertyPrivate::write( return true; } -QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engine, int userType) +bool QQmlPropertyPrivate::reset( + QObject *object, const QQmlPropertyData &property, + QQmlPropertyData::WriteFlags flags) { - QMetaType metaType(userType); - if ((metaType.flags() & QMetaType::PointerToQObject) && metaType.metaObject()) - return metaType.metaObject(); - if (engine) - return engine->rawMetaObjectForType(userType); - QQmlType type = QQmlMetaType::qmlType(userType); - if (type.isValid()) - return QQmlMetaObject(type.baseMetaObject()); - return QQmlMetaObject(); + const BindingFixer bindingFixer(object, property, flags); + property.resetProperty(object, flags); + return true; +} + +QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QMetaType metaType) +{ + if (metaType.flags() & QMetaType::PointerToQObject) { + if (const QMetaObject *metaObject = metaType.metaObject()) + return metaObject; + } + return QQmlMetaType::rawMetaObjectForType(metaType); } /*! @@ -1699,9 +1956,8 @@ QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const Q // If no signal is found, but the signal is of the form "onBlahChanged", // return the notify signal for the property "Blah" - if (name.endsWith("Changed")) { - QByteArray propName = name.mid(0, name.length() - 7); - int propIdx = mo->indexOfProperty(propName.constData()); + if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(name)) { + int propIdx = mo->indexOfProperty(propName->constData()); if (propIdx >= 0) { QMetaProperty prop = mo->property(propIdx); if (prop.hasNotifySignal()) @@ -1712,6 +1968,16 @@ QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const Q return QMetaMethod(); } +/*! + Return the property corresponding to \a name +*/ +QMetaProperty QQmlPropertyPrivate::findPropertyByName(const QMetaObject *mo, const QByteArray &name) +{ + Q_ASSERT(mo); + const int i = mo->indexOfProperty(name); + return i < 0 ? QMetaProperty() : mo->property(i); +} + /*! \internal If \a indexInSignalRange is true, \a index is treated as a signal index (see QObjectPrivate::signalIndex()), otherwise it is treated as a @@ -1721,7 +1987,7 @@ static inline void flush_vme_signal(const QObject *object, int index, bool index { QQmlData *data = QQmlData::get(object); if (data && data->propertyCache) { - QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index) + const QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index) : data->propertyCache->method(index); if (property && property->isVMESignal()) { @@ -1764,3 +2030,5 @@ void QQmlPropertyPrivate::flushSignal(const QObject *sender, int signal_index) } QT_END_NAMESPACE + +#include "moc_qqmlproperty.cpp" |