aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp60
5 files changed, 153 insertions, 15 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;
diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp
index 39af59d0b0..82930b3f4a 100644
--- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp
+++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp
@@ -45,6 +45,7 @@ private slots:
void initTestCase();
void insert();
+ void insertMany();
void operatorInsert();
void operatorValue();
void clear();
@@ -153,6 +154,63 @@ void tst_QQmlPropertyMap::insert()
QCOMPARE(map.value(QLatin1String("valueCHANGED")), QVariant(1));
}
+void tst_QQmlPropertyMap::insertMany()
+{
+ QHash<QString, QVariant> values;
+ values.insert(QLatin1String("key2"), 200);
+ values.insert(QLatin1String("key1"), "Hello World");
+ values.insert(QLatin1String("valueChange"), 1);
+ values.insert(QLatin1String("valueCHANGED"), 1);
+
+ QQmlPropertyMap map;
+ map.insert(values);
+ QCOMPARE(map.keys().count(), 4);
+ QVERIFY(map.contains(QLatin1String("key1")));
+ QCOMPARE(map.value(QLatin1String("key2")), QVariant(200));
+ QCOMPARE(map.value(QLatin1String("key1")), QVariant("Hello World"));
+ //but 'valueChange' should be ok
+ QVERIFY(map.contains(QLatin1String("valueChange")));
+ QCOMPARE(map.value(QLatin1String("valueChange")), QVariant(1));
+ //'valueCHANGED' should be ok, too
+ QVERIFY(map.contains(QLatin1String("valueCHANGED")));
+ QCOMPARE(map.value(QLatin1String("valueCHANGED")), QVariant(1));
+
+ values.insert(QLatin1String("keys"), 1);
+ values.insert(QStringLiteral("foobar"), 12);
+ values[QStringLiteral("key1")] = 100;
+ //inserting property names same with existing method(signal, slot, method) names is not allowed
+ //QQmlPropertyMap has an invokable keys() method
+ QTest::ignoreMessage(QtWarningMsg, "Creating property with name \"keys\" is not permitted, conflicts with internal symbols.");
+ map.insert(values);
+ QCOMPARE(map.keys().count(), 4);
+ QVERIFY(!map.contains(QLatin1String("keys")));
+ QVERIFY(map.value(QLatin1String("keys")).isNull());
+
+ values.remove(QStringLiteral("keys"));
+ values.insert(QLatin1String("deleteLater"), 1);
+ //QQmlPropertyMap has a deleteLater() slot
+ QTest::ignoreMessage(QtWarningMsg, "Creating property with name \"deleteLater\" is not permitted, conflicts with internal symbols.");
+ map.insert(values);
+ QCOMPARE(map.keys().count(), 4);
+ QVERIFY(!map.contains(QLatin1String("deleteLater")));
+ QVERIFY(map.value(QLatin1String("deleteLater")).isNull());
+
+ values.remove(QStringLiteral("deleteLater"));
+ values.insert(QLatin1String("valueChanged"), 1);
+ //QQmlPropertyMap has an valueChanged() signal
+ QTest::ignoreMessage(QtWarningMsg, "Creating property with name \"valueChanged\" is not permitted, conflicts with internal symbols.");
+ map.insert(values);
+ QCOMPARE(map.keys().count(), 4);
+ QVERIFY(!map.contains(QLatin1String("valueChanged")));
+ QVERIFY(map.value(QLatin1String("valueChanged")).isNull());
+
+ values.remove(QStringLiteral("valueChanged"));
+ map.insert(values); // Adds "foobar" and changes "key1"
+ QCOMPARE(map.keys().count(), 5);
+ QCOMPARE(map.value(QStringLiteral("foobar")).toInt(), 12);
+ QCOMPARE(map.value(QStringLiteral("key1")).toInt(), 100);
+}
+
void tst_QQmlPropertyMap::operatorInsert()
{
QQmlPropertyMap map;
@@ -216,7 +274,7 @@ void tst_QQmlPropertyMap::changed()
component.setData("import QtQuick 2.0\nText { text: { testdata.key1 = 'Hello World'; 'X' } }",
QUrl::fromLocalFile(""));
QVERIFY(component.isReady());
- QQuickText *txt = qobject_cast<QQuickText*>(component.create());
+ QScopedPointer<QQuickText> txt(qobject_cast<QQuickText*>(component.create()));
QVERIFY(txt);
QCOMPARE(txt->text(), QString('X'));
QCOMPARE(spy.count(), 1);