diff options
Diffstat (limited to 'src/qml/qml/qqmlproperty.cpp')
-rw-r--r-- | src/qml/qml/qqmlproperty.cpp | 198 |
1 files changed, 103 insertions, 95 deletions
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index ad08f06b05..7c78fbb984 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -2,40 +2,35 @@ // 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 "qqmlboundsignal_p.h" -#include "qqmlcontext.h" -#include "qqmlboundsignal_p.h" -#include "qqmlengine.h" -#include "qqmlengine_p.h" -#include "qqmldata_p.h" -#include "qqmlstringconverters_p.h" - -#include "qqmlvmemetaobject_p.h" -#include "qqmlvaluetypeproxybinding_p.h" + #include <private/qjsvalue_p.h> -#include <private/qv4functionobject_p.h> -#include <private/qv4qobjectwrapper_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 <QtQml/private/qqmllist_p.h> - -#include <QStringList> -#include <QVector> -#include <private/qmetaobject_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/private/qproperty_p.h> #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 @@ -252,10 +247,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, // 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 @@ -267,12 +263,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, // TODO: Do we really _not_ want to query the namespaced types here? r = typeNameCache->query<QQmlTypeNameCache::QueryNamespaced::No>( - path.at(ii), r.importNamespace); + path.at(ii), r.importNamespace, typeLoader); 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 @@ -370,10 +365,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, QQmlData *ddata = QQmlData::get(currentObject, false); auto findChangeSignal = [&](QStringView signalName) { - const QString changed = QStringLiteral("Changed"); - if (signalName.endsWith(changed)) { - const QStringView propName = signalName.first(signalName.size() - changed.size()); - const QQmlPropertyData *d = ddata->propertyCache->property(propName, currentObject, context); + if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(signalName)) { + const QQmlPropertyData *d = + ddata->propertyCache->property(*propName, currentObject, context); while (d && d->isFunction()) d = ddata->propertyCache->overrideData(d); @@ -386,20 +380,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, return false; }; - const QString terminalString = terminal.toString(); - if (QmlIR::IRBuilder::isSignalPropertyName(terminalString)) { - QString signalName = terminalString.mid(2); - int firstNon_; - int length = signalName.size(); - for (firstNon_ = 0; firstNon_ < length; ++firstNon_) - if (signalName.at(firstNon_) != u'_') - break; - signalName[firstNon_] = signalName.at(firstNon_).toLower(); - + const auto findSignal = [&](const QString &signalName) { if (ddata && ddata->propertyCache) { // Try method - const 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 @@ -410,13 +395,30 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, if (d) { object = currentObject; core = *d; - return; + return true; } - if (findChangeSignal(signalName)) - return; - } else if (findSignalInMetaObject(signalName.toUtf8())) { + return findChangeSignal(signalName); + } + + return findSignalInMetaObject(signalName.toUtf8()); + }; + + 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; + } } } @@ -429,7 +431,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, if (!property->isFunction()) { object = currentObject; core = *property; - nameCache = terminalString; + nameCache = terminal.toString(); return; } property = ddata->propertyCache->overrideData(property); @@ -750,15 +752,7 @@ QString QQmlProperty::name() const d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName); } else if (type() & SignalProperty) { // ### Qt7: Return the original signal name here. Do not prepend "on" - QString name = QStringLiteral("on") + d->core.name(d->object); - for (int i = 2, end = name.size(); i != end; ++i) { - const QChar c = name.at(i); - if (c != u'_') { - name[i] = c.toUpper(); - break; - } - } - d->nameCache = name; + d->nameCache = QQmlSignalNames::signalNameToHandlerName(d->core.name(d->object)); } else { d->nameCache = d->core.name(d->object); } @@ -1231,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 @@ -1384,6 +1373,18 @@ static ConvertAndAssignResult tryConvertAndAssign( 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}; + } + } + // common cases: switch (propertyMetaType.id()) { case QMetaType::Bool: @@ -1429,14 +1430,15 @@ static ConvertAndAssignResult tryConvertAndAssign( } } - QVariant converted(propertyMetaType); - if (QQmlValueTypeProvider::createValueType(value, propertyMetaType, converted.data()) - || QMetaType::convert(value.metaType(), value.constData(), - propertyMetaType, converted.data())) { - return {true, property.writeProperty(object, converted.data(), flags)}; + 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 {false, false}; + return {true, property.writeProperty(object, converted.data(), flags)}; }; template<typename Op> @@ -1559,18 +1561,26 @@ bool QQmlPropertyPrivate::write( if (valueMetaObject.isNull()) return false; - QQmlListProperty<void> prop; + QQmlListProperty<QObject> prop; property.readProperty(object, &prop); - if (!prop.clear) + if (!prop.clear || !prop.append) return false; - prop.clear(&prop); + const bool useNonsignalingListOps = prop.clear == &QQmlVMEMetaObject::list_clear + && prop.append == &QQmlVMEMetaObject::list_append; + + auto propClear = + useNonsignalingListOps ? &QQmlVMEMetaObject::list_clear_nosignal : prop.clear; + auto propAppend = + useNonsignalingListOps ? &QQmlVMEMetaObject::list_append_nosignal : prop.append; + + propClear(&prop); 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>()) { @@ -1581,9 +1591,18 @@ bool QQmlPropertyPrivate::write( 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); @@ -1595,16 +1614,6 @@ bool QQmlPropertyPrivate::write( sequence.addValue(list.data(), value.data()); property.writeProperty(object, list.data(), flags); } - } else if (variantMetaType == QMetaType::fromType<QJSValue>()) { - QJSValue jsValue = qvariant_cast<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; - } - return false; } else if (enginePriv && propertyMetaType == QMetaType::fromType<QJSValue>()) { // We can convert everything into a QJSValue if we have an engine. QJSValue jsValue = QJSValuePrivate::fromReturnedValue( @@ -1696,13 +1705,13 @@ bool QQmlPropertyPrivate::write( if (!ok && QQmlMetaType::isInterface(propertyMetaType)) { auto valueAsQObject = qvariant_cast<QObject *>(value); - if (void *interface = valueAsQObject + 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); } } @@ -1947,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.size() - 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()) |