aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlproperty.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlproperty.cpp')
-rw-r--r--src/qml/qml/qqmlproperty.cpp970
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"