diff options
Diffstat (limited to 'src/qmlmodels/qqmldmlistaccessordata_p.h')
-rw-r--r-- | src/qmlmodels/qqmldmlistaccessordata_p.h | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/qmlmodels/qqmldmlistaccessordata_p.h b/src/qmlmodels/qqmldmlistaccessordata_p.h new file mode 100644 index 0000000000..e02c3a88f6 --- /dev/null +++ b/src/qmlmodels/qqmldmlistaccessordata_p.h @@ -0,0 +1,293 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQMLDMLISTACCESSORDATA_P_H +#define QQMLDMLISTACCESSORDATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qqmladaptormodelenginedata_p.h> +#include <private/qqmldelegatemodel_p_p.h> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class VDMListDelegateDataType; + +class QQmlDMListAccessorData : public QQmlDelegateModelItem +{ + Q_OBJECT + Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) + QT_ANONYMOUS_PROPERTY(QVariant READ modelData WRITE setModelData NOTIFY modelDataChanged FINAL) +public: + QQmlDMListAccessorData( + const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType, + VDMListDelegateDataType *dataType, int index, int row, int column, + const QVariant &value); + ~QQmlDMListAccessorData(); + + QVariant modelData() const + { + return cachedData; + } + + void setModelData(const QVariant &data); + + static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) + { + QV4::ExecutionEngine *v4 = b->engine(); + const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>(); + if (!o) + return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); + + return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData); + } + + static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) + { + QV4::ExecutionEngine *v4 = b->engine(); + const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>(); + if (!o) + return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); + if (!argc) + return v4->throwTypeError(); + + static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData( + QV4::ExecutionEngine::toVariant(argv[0], QMetaType {})); + return QV4::Encode::undefined(); + } + + QV4::ReturnedValue get() override + { + QQmlAdaptorModelEngineData *data = QQmlAdaptorModelEngineData::get(v4); + QV4::Scope scope(v4); + QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); + QV4::ScopedObject p(scope, data->listItemProto.value()); + o->setPrototypeOf(p); + ++scriptRef; + return o.asReturnedValue(); + } + + void setValue(const QString &role, const QVariant &value) override; + bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; + +Q_SIGNALS: + void modelDataChanged(); + +private: + friend class VDMListDelegateDataType; + QVariant cachedData; + + // Gets cleaned when the metaobject has processed it. + bool cachedDataClean = false; +}; + + +class VDMListDelegateDataType final + : public QQmlRefCounted<VDMListDelegateDataType> + , public QQmlAdaptorModel::Accessors + , public QAbstractDynamicMetaObject +{ +public: + VDMListDelegateDataType(QQmlAdaptorModel *model) + : model(model) + { + QQmlAdaptorModelEngineData::setModelDataType<QQmlDMListAccessorData>(&builder, this); + metaObject.reset(builder.toMetaObject()); + *static_cast<QMetaObject *>(this) = *metaObject.data(); + } + + void cleanup(QQmlAdaptorModel &) const override + { + release(); + } + + int rowCount(const QQmlAdaptorModel &model) const override + { + return model.list.count(); + } + + int columnCount(const QQmlAdaptorModel &model) const override + { + switch (model.list.type()) { + case QQmlListAccessor::Invalid: + return 0; + case QQmlListAccessor::StringList: + case QQmlListAccessor::UrlList: + case QQmlListAccessor::Integer: + return 1; + default: + break; + } + + // If there are no properties, we can get modelData itself. + return std::max(1, propertyCount() - propertyOffset); + } + + static const QMetaObject *metaObjectFromType(QMetaType type) + { + if (const QMetaObject *metaObject = type.metaObject()) + return metaObject; + + // NB: This acquires the lock on QQmlMetaTypeData. If we had a QQmlEngine here, + // we could use QQmlGadgetPtrWrapper::instance() to avoid this. + if (const QQmlValueType *valueType = QQmlMetaType::valueType(type)) + return valueType->staticMetaObject(); + + return nullptr; + } + + template<typename String> + static QString toQString(const String &string) + { + if constexpr (std::is_same_v<String, QString>) + return string; + else if constexpr (std::is_same_v<String, QByteArray>) + return QString::fromUtf8(string); + else if constexpr (std::is_same_v<String, const char *>) + return QString::fromUtf8(string); + Q_UNREACHABLE_RETURN(QString()); + } + + template<typename String> + static QByteArray toUtf8(const String &string) + { + if constexpr (std::is_same_v<String, QString>) + return string.toUtf8(); + else if constexpr (std::is_same_v<String, QByteArray>) + return string; + else if constexpr (std::is_same_v<String, const char *>) + return QByteArray::fromRawData(string, qstrlen(string)); + Q_UNREACHABLE_RETURN(QByteArray()); + } + + template<typename String> + static QVariant value(const QVariant *row, const String &role) + { + const QMetaType type = row->metaType(); + if (type == QMetaType::fromType<QVariantMap>()) + return row->toMap().value(toQString(role)); + + if (type == QMetaType::fromType<QVariantHash>()) + return row->toHash().value(toQString(role)); + + const QMetaType::TypeFlags typeFlags = type.flags(); + if (typeFlags & QMetaType::PointerToQObject) + return row->value<QObject *>()->property(toUtf8(role)); + + if (const QMetaObject *metaObject = metaObjectFromType(type)) { + const int propertyIndex = metaObject->indexOfProperty(toUtf8(role)); + if (propertyIndex >= 0) + return metaObject->property(propertyIndex).readOnGadget(row->constData()); + } + + return QVariant(); + } + + template<typename String> + void createPropertyIfMissing(const String &string) + { + for (int i = 0, end = propertyCount(); i < end; ++i) { + if (QAnyStringView(property(i).name()) == QAnyStringView(string)) + return; + } + + createProperty(toUtf8(string), nullptr); + } + + void createMissingProperties(const QVariant *row) + { + const QMetaType type = row->metaType(); + if (type == QMetaType::fromType<QVariantMap>()) { + const QVariantMap map = row->toMap(); + for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it) + createPropertyIfMissing(*it); + } else if (type == QMetaType::fromType<QVariantHash>()) { + const QVariantHash map = row->toHash(); + for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it) + createPropertyIfMissing(*it); + } else if (type.flags() & QMetaType::PointerToQObject) { + const QMetaObject *metaObject = row->value<QObject *>()->metaObject(); + for (int i = 0, end = metaObject->propertyCount(); i < end; ++i) + createPropertyIfMissing(metaObject->property(i).name()); + } else if (const QMetaObject *metaObject = metaObjectFromType(type)) { + for (int i = 0, end = metaObject->propertyCount(); i < end; ++i) + createPropertyIfMissing(metaObject->property(i).name()); + } + } + + template<typename String> + static void setValue(QVariant *row, const String &role, const QVariant &value) + { + const QMetaType type = row->metaType(); + if (type == QMetaType::fromType<QVariantMap>()) { + static_cast<QVariantMap *>(row->data())->insert(toQString(role), value); + } else if (type == QMetaType::fromType<QVariantHash>()) { + static_cast<QVariantHash *>(row->data())->insert(toQString(role), value); + } else if (type.flags() & QMetaType::PointerToQObject) { + row->value<QObject *>()->setProperty(toUtf8(role), value); + } else if (const QMetaObject *metaObject = metaObjectFromType(type)) { + const int propertyIndex = metaObject->indexOfProperty(toUtf8(role)); + if (propertyIndex >= 0) + metaObject->property(propertyIndex).writeOnGadget(row->data(), value); + } + } + + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override + { + const QVariant entry = model.list.at(index); + if (role == QLatin1String("modelData") || role.isEmpty()) + return entry; + + return value(&entry, role); + } + + QQmlDelegateModelItem *createItem( + QQmlAdaptorModel &model, + const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType, + int index, int row, int column) override + { + const QVariant value = (index >= 0 && index < model.list.count()) + ? model.list.at(index) + : QVariant(); + return new QQmlDMListAccessorData(metaType, this, index, row, column, value); + } + + bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override + { + for (auto modelItem : items) { + const int modelItemIndex = modelItem->index; + if (modelItemIndex < index || modelItemIndex >= index + count) + continue; + + auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem); + QVariant updatedModelData = model.list.at(listModelItem->index); + listModelItem->setModelData(updatedModelData); + } + return true; + } + + void emitAllSignals(QQmlDMListAccessorData *accessor) const; + + int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) final; + int createProperty(const char *name, const char *) final; + QMetaObject *toDynamicMetaObject(QObject *accessors) final; + + QMetaObjectBuilder builder; + QQmlAdaptorModel *model = nullptr; + int propertyOffset = 0; + int signalOffset = 0; +}; + +QT_END_NAMESPACE + +#endif // QQMLDMLISTACCESSORDATA_P_H |