diff options
Diffstat (limited to 'src/qmlmodels/qqmldmabstractitemmodeldata_p.h')
-rw-r--r-- | src/qmlmodels/qqmldmabstractitemmodeldata_p.h | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/src/qmlmodels/qqmldmabstractitemmodeldata_p.h b/src/qmlmodels/qqmldmabstractitemmodeldata_p.h new file mode 100644 index 0000000000..e3769b6d98 --- /dev/null +++ b/src/qmlmodels/qqmldmabstractitemmodeldata_p.h @@ -0,0 +1,334 @@ +// 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 QQMLDMABSTRACTITEMMODELDATA_P_H +#define QQMLDMABSTRACTITEMMODELDATA_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 VDMAbstractItemModelDataType; +class QQmlDMAbstractItemModelData : public QQmlDelegateModelItem +{ + Q_OBJECT + Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) + Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) + QT_ANONYMOUS_PROPERTY(QVariant READ modelData NOTIFY modelDataChanged FINAL) + +public: + QQmlDMAbstractItemModelData( + const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType, + VDMAbstractItemModelDataType *dataType, + int index, int row, int column); + + int metaCall(QMetaObject::Call call, int id, void **arguments); + bool hasModelChildren() const; + + QV4::ReturnedValue get() override; + void setValue(const QString &role, const QVariant &value) override; + bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; + + static QV4::ReturnedValue get_property( + const QV4::FunctionObject *b, const QV4::Value *thisObject, + const QV4::Value *argv, int argc); + static QV4::ReturnedValue set_property( + const QV4::FunctionObject *b, const QV4::Value *thisObject, + const QV4::Value *argv, int argc); + + static QV4::ReturnedValue get_modelData( + const QV4::FunctionObject *b, const QV4::Value *thisObject, + const QV4::Value *argv, int argc); + static QV4::ReturnedValue set_modelData( + const QV4::FunctionObject *b, const QV4::Value *thisObject, + const QV4::Value *argv, int argc); + + QVariant modelData() const; + void setModelData(const QVariant &modelData); + + const VDMAbstractItemModelDataType *type() const { return m_type; } + +Q_SIGNALS: + void modelDataChanged(); + +private: + QVariant value(int role) const; + void setValue(int role, const QVariant &value); + + VDMAbstractItemModelDataType *m_type; + QVector<QVariant> m_cachedData; +}; + +class VDMAbstractItemModelDataType final + : public QQmlRefCounted<VDMAbstractItemModelDataType> + , public QQmlAdaptorModel::Accessors + , public QAbstractDynamicMetaObject +{ +public: + VDMAbstractItemModelDataType(QQmlAdaptorModel *model) + : model(model) + , propertyOffset(0) + , signalOffset(0) + { + } + + bool notify( + const QQmlAdaptorModel &, + const QList<QQmlDelegateModelItem *> &items, + int index, + int count, + const QVector<int> &roles) const override + { + bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); + if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { + QList<int> roleIds; + for (const QByteArray &r : watchedRoles) { + QHash<QByteArray, int>::const_iterator it = roleNames.find(r); + if (it != roleNames.end()) + roleIds << it.value(); + } + const_cast<VDMAbstractItemModelDataType *>(this)->watchedRoleIds = roleIds; + } + + QVector<int> signalIndexes; + for (int i = 0; i < roles.size(); ++i) { + const int role = roles.at(i); + if (!changed && watchedRoleIds.contains(role)) + changed = true; + + int propertyId = propertyRoles.indexOf(role); + if (propertyId != -1) + signalIndexes.append(propertyId + signalOffset); + } + if (roles.isEmpty()) { + const int propertyRolesCount = propertyRoles.size(); + signalIndexes.reserve(propertyRolesCount); + for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId) + signalIndexes.append(propertyId + signalOffset); + } + + QVarLengthArray<QQmlGuard<QQmlDMAbstractItemModelData>> guardedItems; + for (const auto item : items) { + Q_ASSERT(qobject_cast<QQmlDMAbstractItemModelData *>(item) == item); + guardedItems.append(static_cast<QQmlDMAbstractItemModelData *>(item)); + } + + for (const auto &item : std::as_const(guardedItems)) { + if (item.isNull()) + continue; + + const int idx = item->modelIndex(); + if (idx >= index && idx < index + count) { + for (int i = 0; i < signalIndexes.size(); ++i) + QMetaObject::activate(item, signalIndexes.at(i), nullptr); + emit item->modelDataChanged(); + } + } + return changed; + } + + void replaceWatchedRoles( + QQmlAdaptorModel &, + const QList<QByteArray> &oldRoles, + const QList<QByteArray> &newRoles) const override + { + VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); + + dataType->watchedRoleIds.clear(); + for (const QByteArray &oldRole : oldRoles) + dataType->watchedRoles.removeOne(oldRole); + dataType->watchedRoles += newRoles; + } + + static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) + { + QV4::Scope scope(b); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); + if (!o) + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"))); + + const QQmlAdaptorModel *const model + = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->type()->model; + if (o->d()->item->index >= 0) { + if (const QAbstractItemModel *const aim = model->aim()) + RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex)))); + } + RETURN_RESULT(QV4::Encode(false)); + } + + + void initializeConstructor(QQmlAdaptorModelEngineData *const data) + { + QV4::ExecutionEngine *v4 = data->v4; + QV4::Scope scope(v4); + QV4::ScopedObject proto(scope, v4->newObject()); + proto->defineAccessorProperty(QStringLiteral("index"), QQmlAdaptorModelEngineData::get_index, nullptr); + proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, nullptr); + proto->defineAccessorProperty(QStringLiteral("modelData"), + QQmlDMAbstractItemModelData::get_modelData, + QQmlDMAbstractItemModelData::set_modelData); + QV4::ScopedProperty p(scope); + + typedef QHash<QByteArray, int>::const_iterator iterator; + for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) { + const qsizetype propertyId = propertyRoles.indexOf(it.value()); + const QByteArray &propertyName = it.key(); + + QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName))); + QV4::ExecutionContext *global = v4->rootContext(); + QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMAbstractItemModelData::get_property)); + QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMAbstractItemModelData::set_property)); + p->setGetter(g); + p->setSetter(s); + proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); + } + prototype.set(v4, proto); + } + + // QAbstractDynamicMetaObject + + void objectDestroyed(QObject *) override + { + release(); + } + + int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override + { + return static_cast<QQmlDMAbstractItemModelData *>(object)->metaCall(call, id, arguments); + } + + int rowCount(const QQmlAdaptorModel &model) const override + { + if (const QAbstractItemModel *aim = model.aim()) + return aim->rowCount(model.rootIndex); + return 0; + } + + int columnCount(const QQmlAdaptorModel &model) const override + { + if (const QAbstractItemModel *aim = model.aim()) + return aim->columnCount(model.rootIndex); + return 0; + } + + void cleanup(QQmlAdaptorModel &) const override + { + release(); + } + + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override + { + if (!metaObject) { + VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); + dataType->initializeMetaType(model); + } + + if (const QAbstractItemModel *aim = model.aim()) { + const QModelIndex modelIndex + = aim->index(model.rowAt(index), model.columnAt(index), model.rootIndex); + + const auto it = roleNames.find(role.toUtf8()), end = roleNames.end(); + if (it != roleNames.end()) + return modelIndex.data(*it); + + if (role.isEmpty() || role == QLatin1String("modelData")) { + if (roleNames.size() == 1) + return modelIndex.data(roleNames.begin().value()); + + QVariantMap modelData; + for (auto jt = roleNames.begin(); jt != end; ++jt) + modelData.insert(QString::fromUtf8(jt.key()), modelIndex.data(jt.value())); + return modelData; + } + + if (role == QLatin1String("hasModelChildren")) + return QVariant(aim->hasChildren(modelIndex)); + } + return QVariant(); + } + + QVariant parentModelIndex(const QQmlAdaptorModel &model) const override + { + if (const QAbstractItemModel *aim = model.aim()) + return QVariant::fromValue(aim->parent(model.rootIndex)); + return QVariant(); + } + + QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override + { + if (const QAbstractItemModel *aim = model.aim()) + return QVariant::fromValue(aim->index(model.rowAt(index), model.columnAt(index), + model.rootIndex)); + return QVariant(); + } + + bool canFetchMore(const QQmlAdaptorModel &model) const override + { + if (const QAbstractItemModel *aim = model.aim()) + return aim->canFetchMore(model.rootIndex); + return false; + } + + void fetchMore(QQmlAdaptorModel &model) const override + { + if (QAbstractItemModel *aim = model.aim()) + aim->fetchMore(model.rootIndex); + } + + QQmlDelegateModelItem *createItem( + QQmlAdaptorModel &model, + const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType, + int index, int row, int column) override + { + if (!metaObject) + initializeMetaType(model); + return new QQmlDMAbstractItemModelData(metaType, this, index, row, column); + } + + void initializeMetaType(const QQmlAdaptorModel &model) + { + QMetaObjectBuilder builder; + QQmlAdaptorModelEngineData::setModelDataType<QQmlDMAbstractItemModelData>(&builder, this); + + const QByteArray propertyType = QByteArrayLiteral("QVariant"); + const QAbstractItemModel *aim = model.aim(); + const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>(); + for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) { + const int propertyId = propertyRoles.size(); + propertyRoles.append(it.key()); + roleNames.insert(it.value(), it.key()); + QQmlAdaptorModelEngineData::addProperty(&builder, propertyId, it.value(), propertyType); + } + + metaObject.reset(builder.toMetaObject()); + *static_cast<QMetaObject *>(this) = *metaObject; + propertyCache = QQmlPropertyCache::createStandalone( + metaObject.data(), model.modelItemRevision); + } + + QV4::PersistentValue prototype; + QList<int> propertyRoles; + QList<int> watchedRoleIds; + QList<QByteArray> watchedRoles; + QHash<QByteArray, int> roleNames; + QQmlAdaptorModel *model; + int propertyOffset; + int signalOffset; +}; + +QT_END_NAMESPACE + +#endif // QQMLDMABSTRACTITEMMODELDATA_P_H |