aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-01-13 11:50:07 +0100
committerUlf Hermann <ulf.hermann@qt.io>2021-01-13 14:32:11 +0100
commitb64f8dacae36fca948933cf56498d5e4ad3e2a07 (patch)
tree3c41c0270d6b4ab13b104581b0f1024e0e3cddab /src/qml
parent315261a809778a8ac37c523741e021d6431ab85e (diff)
QQmlPropertyMap: Add a method to insert multiple values at once
This avoid re-building the metaobject for every property added. As rebuilding the metaobject is an effort linear in the number of properties, the runtime when adding multiple properties via singular insert() is quadratic in the number of properties. The plural insert() rebuilds the metaobject only once. Task-number: QTBUG-57792 Change-Id: I9513c4de047724e4141dab72aacfbdd840a3e465 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp74
-rw-r--r--src/qml/qml/qqmlopenmetaobject_p.h3
-rw-r--r--src/qml/util/qqmlpropertymap.cpp30
-rw-r--r--src/qml/util/qqmlpropertymap.h1
4 files changed, 94 insertions, 14 deletions
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index 5b0d306c0e..5319700d85 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -236,11 +236,21 @@ public:
return data[idx].valueSet;
}
+ void dropPropertyCache() {
+ if (QQmlData *ddata = QQmlData::get(object, /*create*/false)) {
+ if (ddata->propertyCache) {
+ ddata->propertyCache->release();
+ ddata->propertyCache = nullptr;
+ }
+ }
+ }
+
QQmlOpenMetaObject *q;
QAbstractDynamicMetaObject *parent = nullptr;
QVector<Property> data;
QObject *object;
QQmlRefPointer<QQmlOpenMetaObjectType> type;
+ QVector<QByteArray> *deferredPropertyNames = nullptr;
bool autoCreate;
bool cacheProperties = false;
};
@@ -322,6 +332,16 @@ QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const
return d->parent;
}
+bool QQmlOpenMetaObject::checkedSetValue(int index, const QVariant &value, bool force)
+{
+ if (!force && d->propertyValue(index) == value)
+ return false;
+
+ d->setPropertyValue(index, value);
+ activate(d->object, index + d->type->d->signalOffset, nullptr);
+ return true;
+}
+
QVariant QQmlOpenMetaObject::value(int id) const
{
return d->propertyValue(id);
@@ -361,16 +381,43 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, b
id = *iter;
}
- if (id >= 0) {
- if (!force && d->propertyValue(id) == val)
- return false;
+ if (id >= 0)
+ return checkedSetValue(id, val, force);
+
+ return false;
+}
- d->setPropertyValue(id, val);
- activate(d->object, id + d->type->d->signalOffset, nullptr);
- return true;
+void QQmlOpenMetaObject::setValues(const QHash<QByteArray, QVariant> &values, bool force)
+{
+ QVector<QByteArray> missingProperties;
+ d->deferredPropertyNames = &missingProperties;
+ const auto &names = d->type->d->names;
+
+ for (auto valueIt = values.begin(), end = values.end(); valueIt != end; ++valueIt) {
+ const auto nameIt = names.constFind(valueIt.key());
+ if (nameIt == names.constEnd()) {
+ const int id = createProperty(valueIt.key(), "") - d->type->d->propertyOffset;
+
+ // If id >= 0 some override of createProperty() created it. Then set it.
+ // Else it either ends up in missingProperties and we create it later
+ // or it cannot be created.
+
+ if (id >= 0)
+ checkedSetValue(id, valueIt.value(), force);
+ } else {
+ checkedSetValue(*nameIt, valueIt.value(), force);
+ }
}
- return false;
+ d->deferredPropertyNames = nullptr;
+ if (missingProperties.isEmpty())
+ return;
+
+ d->type->createProperties(missingProperties);
+ d->dropPropertyCache();
+
+ for (const QByteArray &name : qAsConst(missingProperties))
+ checkedSetValue(names[name], values[name], force);
}
// returns true if this value has been initialized by a call to either value() or setValue()
@@ -403,15 +450,14 @@ void QQmlOpenMetaObject::setCached(bool c)
int QQmlOpenMetaObject::createProperty(const char *name, const char *)
{
if (d->autoCreate) {
- int result = d->type->createProperty(name);
-
- if (QQmlData *ddata = QQmlData::get(d->object, /*create*/false)) {
- if (ddata->propertyCache) {
- ddata->propertyCache->release();
- ddata->propertyCache = nullptr;
- }
+ if (d->deferredPropertyNames) {
+ // Defer the creation of new properties. See setValues(QHash<QByteArray, QVariant>)
+ d->deferredPropertyNames->append(name);
+ return -1;
}
+ const int result = d->type->createProperty(name);
+ d->dropPropertyCache();
return result;
} else
return -1;
diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h
index 168a2a6f7f..441556a079 100644
--- a/src/qml/qml/qqmlopenmetaobject_p.h
+++ b/src/qml/qml/qqmlopenmetaobject_p.h
@@ -101,6 +101,7 @@ public:
QVariant value(const QByteArray &) const;
bool setValue(const QByteArray &, const QVariant &, bool force = false);
+ void setValues(const QHash<QByteArray, QVariant> &, bool force = false);
QVariant value(int) const;
void setValue(int, const QVariant &);
QVariant &valueRef(const QByteArray &);
@@ -132,6 +133,8 @@ protected:
QAbstractDynamicMetaObject *parent() const;
+ bool checkedSetValue(int index, const QVariant &value, bool force);
+
private:
QQmlOpenMetaObjectPrivate *d;
friend class QQmlOpenMetaObjectType;
diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp
index e5fa66aded..e38cf3a2a9 100644
--- a/src/qml/util/qqmlpropertymap.cpp
+++ b/src/qml/util/qqmlpropertymap.cpp
@@ -234,6 +234,36 @@ void QQmlPropertyMap::insert(const QString &key, const QVariant &value)
}
/*!
+ \since 6.1
+
+ Inserts the \a values into the QQmlPropertyMap.
+
+ Keys that don't exist are automatically created.
+
+ This method is substantially faster than calling \c{insert(key, value)}
+ many times in a row.
+*/
+void QQmlPropertyMap::insert(const QVariantHash &values)
+{
+ Q_D(QQmlPropertyMap);
+
+ QHash<QByteArray, QVariant> checkedValues;
+ for (auto it = values.begin(), end = values.end(); it != end; ++it) {
+ const QString &key = it.key();
+ if (!d->validKeyName(key)) {
+ qWarning() << "Creating property with name"
+ << key
+ << "is not permitted, conflicts with internal symbols.";
+ return;
+ }
+
+ checkedValues.insert(key.toUtf8(), it.value());
+ }
+ d->mo->setValues(checkedValues);
+
+}
+
+/*!
Returns the list of keys.
Keys that have been cleared will still appear in this list, even though their
diff --git a/src/qml/util/qqmlpropertymap.h b/src/qml/util/qqmlpropertymap.h
index d948989833..556754c021 100644
--- a/src/qml/util/qqmlpropertymap.h
+++ b/src/qml/util/qqmlpropertymap.h
@@ -60,6 +60,7 @@ public:
QVariant value(const QString &key) const;
void insert(const QString &key, const QVariant &value);
+ void insert(const QVariantHash &values);
void clear(const QString &key);
Q_INVOKABLE QStringList keys() const;