#include "variants.hpp" #include #include #include #include #include #include #include #include #include #include #include "reload.hpp" void Variants::onReload(QObject* oldInstance) { auto* old = qobject_cast(oldInstance); for (auto& [variant, instanceObj]: this->mInstances.values) { QObject* oldInstance = nullptr; if (old != nullptr) { auto& values = old->mInstances.values; if (variant.canConvert()) { auto variantMap = variant.value(); int matchcount = 0; int matchi = 0; int i = 0; for (auto& [value, _]: values) { if (!value.canConvert()) continue; auto valueSet = value.value(); int count = 0; for (auto [k, v]: variantMap.asKeyValueRange()) { if (valueSet.contains(k) && valueSet.value(k) == v) { count++; } } if (count > matchcount) { matchcount = count; matchi = i; } i++; } if (matchcount > 0) { oldInstance = values.takeAt(matchi).second; } } else { int i = 0; for (auto& [value, _]: values) { if (variant == value) { oldInstance = values.takeAt(i).second; break; } i++; } } } auto* instance = qobject_cast(instanceObj); if (instance != nullptr) instance->onReload(oldInstance); else Reloadable::reloadChildrenRecursive(instanceObj, oldInstance); } this->loaded = true; } QVariant Variants::model() const { return QVariant::fromValue(this->mModel); } void Variants::setModel(const QVariant& model) { if (model.canConvert()) { this->mModel = model.value(); } else if (model.canConvert()) { auto list = model.value(); if (!list.isReadable()) { qWarning() << "Non readable list" << model << "assigned to Variants.model, Ignoring."; return; } QVariantList model; auto size = list.count(); for (auto i = 0; i < size; i++) { model.push_back(QVariant::fromValue(list.at(i))); } this->mModel = std::move(model); } else { qWarning() << "Non list data" << model << "assigned to Variants.model, Ignoring."; return; } this->updateVariants(); emit this->modelChanged(); emit this->instancesChanged(); } QQmlListProperty Variants::instances() { return QQmlListProperty(this, nullptr, &Variants::instanceCount, &Variants::instanceAt); } qsizetype Variants::instanceCount(QQmlListProperty* prop) { return static_cast(prop->object)->mInstances.values.length(); // NOLINT } QObject* Variants::instanceAt(QQmlListProperty* prop, qsizetype i) { return static_cast(prop->object)->mInstances.values.at(i).second; // NOLINT } void Variants::componentComplete() { this->Reloadable::componentComplete(); this->updateVariants(); } void Variants::updateVariants() { if (this->mDelegate == nullptr) { qWarning() << "Variants instance does not have a component specified"; return; } // clean up removed entries for (auto iter = this->mInstances.values.begin(); iter < this->mInstances.values.end();) { if (this->mModel.contains(iter->first)) { iter++; } else { iter->second->deleteLater(); iter = this->mInstances.values.erase(iter); } } for (auto iter = this->mModel.begin(); iter < this->mModel.end(); iter++) { auto& variant = *iter; for (auto iter2 = this->mModel.begin(); iter2 < iter; iter2++) { if (*iter2 == variant) { qWarning() << "same value specified twice in Variants, duplicates will be ignored:" << variant; goto outer; } } { if (this->mInstances.contains(variant)) { continue; // we dont need to recreate this one } auto variantMap = QVariantMap(); variantMap.insert("modelData", variant); auto* instance = this->mDelegate->createWithInitialProperties( variantMap, QQmlEngine::contextForObject(this->mDelegate) ); if (instance == nullptr) { qWarning() << this->mDelegate->errorString().toStdString().c_str(); qWarning() << "failed to create variant with object" << variant; continue; } QQmlEngine::setObjectOwnership(instance, QQmlEngine::CppOwnership); instance->setParent(this); this->mInstances.insert(variant, instance); if (this->loaded) { if (auto* reloadable = qobject_cast(instance)) reloadable->onReload(nullptr); else Reloadable::reloadChildrenRecursive(instance, nullptr); } } outer:; } } template bool AwfulMap::contains(const K& key) const { return std::ranges::any_of(this->values, [&](const QPair& pair) { return pair.first == key; }); } template V* AwfulMap::get(const K& key) { for (auto& [k, v]: this->values) { if (key == k) { return &v; } } return nullptr; } template void AwfulMap::insert(K key, V value) { this->values.push_back(QPair(key, value)); } template bool AwfulMap::remove(const K& key) { for (auto iter = this->values.begin(); iter < this->values.end(); iter++) { if (iter->first == key) { this->values.erase(iter); return true; } } return false; }