diff --git a/src/core/scriptmodel.cpp b/src/core/scriptmodel.cpp index 244ba9fe..df9776d1 100644 --- a/src/core/scriptmodel.cpp +++ b/src/core/scriptmodel.cpp @@ -18,6 +18,27 @@ void ScriptModel::updateValuesUnique(const QVariantList& newValues) { auto iter = this->mValues.begin(); auto newIter = newValues.begin(); + // TODO: cache this + auto getCmpKey = [&](const QVariant& v) { + if (v.canConvert()) { + auto vMap = v.value(); + if (vMap.contains(this->cmpKey)) { + return vMap.value(this->cmpKey); + } + } + + return v; + }; + + auto variantCmp = [&](const QVariant& a, const QVariant& b) { + if (!this->cmpKey.isEmpty()) return getCmpKey(a) == getCmpKey(b); + else return a == b; + }; + + auto eqPredicate = [&](const QVariant& b) { + return [&](const QVariant& a) { return variantCmp(a, b); }; + }; + while (true) { if (newIter == newValues.end()) { if (iter == this->mValues.end()) break; @@ -40,18 +61,19 @@ void ScriptModel::updateValuesUnique(const QVariantList& newValues) { this->endInsertRows(); break; - } else if (*newIter != *iter) { - auto oldIter = std::find(iter, this->mValues.end(), *newIter); + } else if (!variantCmp(*newIter, *iter)) { + auto oldIter = std::find_if(iter, this->mValues.end(), eqPredicate(*newIter)); if (oldIter != this->mValues.end()) { - if (std::find(newIter, newValues.end(), *iter) == newValues.end()) { + if (std::find_if(newIter, newValues.end(), eqPredicate(*iter)) == newValues.end()) { // Remove any entries we would otherwise move around that aren't in the new list. auto startIter = iter; do { ++iter; } while (iter != this->mValues.end() - && std::find(newIter, newValues.end(), *iter) == newValues.end()); + && std::find_if(newIter, newValues.end(), eqPredicate(*iter)) == newValues.end() + ); auto index = static_cast(std::distance(this->mValues.begin(), iter)); auto startIndex = static_cast(std::distance(this->mValues.begin(), startIter)); @@ -66,7 +88,7 @@ void ScriptModel::updateValuesUnique(const QVariantList& newValues) { ++oldIter; ++newIter; } while (oldIter != this->mValues.end() && newIter != newValues.end() - && *oldIter == *newIter); + && variantCmp(*oldIter, *newIter)); auto index = static_cast(std::distance(this->mValues.begin(), iter)); auto oldStartIndex = @@ -90,7 +112,8 @@ void ScriptModel::updateValuesUnique(const QVariantList& newValues) { do { newIter++; } while (newIter != newValues.end() - && std::find(iter, this->mValues.end(), *newIter) == this->mValues.end()); + && std::find_if(iter, this->mValues.end(), eqPredicate(*newIter)) + == this->mValues.end()); auto index = static_cast(std::distance(this->mValues.begin(), iter)); auto newIndex = static_cast(std::distance(newValues.begin(), newIter)); @@ -123,6 +146,13 @@ void ScriptModel::setValues(const QVariantList& newValues) { emit this->valuesChanged(); } +void ScriptModel::setObjectProp(const QString& objectProp) { + if (objectProp == this->cmpKey) return; + this->cmpKey = objectProp; + this->updateValuesUnique(this->mValues); + emit this->objectPropChanged(); +} + qint32 ScriptModel::rowCount(const QModelIndex& parent) const { if (parent != QModelIndex()) return 0; return static_cast(this->mValues.length()); diff --git a/src/core/scriptmodel.hpp b/src/core/scriptmodel.hpp index 9bbf62d9..10916f6c 100644 --- a/src/core/scriptmodel.hpp +++ b/src/core/scriptmodel.hpp @@ -67,6 +67,13 @@ class ScriptModel: public QAbstractListModel { /// > } /// > ``` Q_PROPERTY(QVariantList values READ values WRITE setValues NOTIFY valuesChanged); + /// The property that javascript objects passed into the model will be compared with. + /// + /// For example, if `objectProp` is `"myprop"` then `{ myprop: "a", other: "y" }` and + /// `{ myprop: "a", other: "z" }` will be considered equal. + /// + /// Defaults to `""`, meaning no key. + Q_PROPERTY(QString objectProp READ objectProp WRITE setObjectProp NOTIFY objectPropChanged); QML_ELEMENT; public: @@ -79,15 +86,20 @@ public: void setValues(const QVariantList& newValues); + [[nodiscard]] QString objectProp() const { return this->cmpKey; } + void setObjectProp(const QString& objectProp); + [[nodiscard]] qint32 rowCount(const QModelIndex& parent) const override; [[nodiscard]] QVariant data(const QModelIndex& index, qint32 role) const override; [[nodiscard]] QHash roleNames() const override; signals: void valuesChanged(); + void objectPropChanged(); private: QVariantList mValues; + QString cmpKey; bool hasActiveIterators = false; void updateValuesUnique(const QVariantList& newValues);