diff options
-rw-r--r-- | src/qml/qml/qqmlopenmetaobject.cpp | 74 | ||||
-rw-r--r-- | src/qml/qml/qqmlopenmetaobject_p.h | 3 | ||||
-rw-r--r-- | src/qml/util/qqmlpropertymap.cpp | 30 | ||||
-rw-r--r-- | src/qml/util/qqmlpropertymap.h | 1 | ||||
-rw-r--r-- | tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp | 60 |
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); |