// 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 #include #include 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 &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 m_cachedData; }; class VDMAbstractItemModelDataType final : public QQmlRefCounted , public QQmlAdaptorModel::Accessors , public QAbstractDynamicMetaObject { public: VDMAbstractItemModelDataType(QQmlAdaptorModel *model) : model(model) , propertyOffset(0) , signalOffset(0) { } bool notify( const QQmlAdaptorModel &, const QList &items, int index, int count, const QVector &roles) const override { bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { QList roleIds; for (const QByteArray &r : watchedRoles) { QHash::const_iterator it = roleNames.find(r); if (it != roleNames.end()) roleIds << it.value(); } const_cast(this)->watchedRoleIds = roleIds; } QVector 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> guardedItems; for (const auto item : items) { Q_ASSERT(qobject_cast(item) == item); guardedItems.append(static_cast(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 &oldRoles, const QList &newRoles) const override { VDMAbstractItemModelDataType *dataType = const_cast(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 o(scope, thisObject->as()); if (!o) RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"))); const QQmlAdaptorModel *const model = static_cast(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::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(global, propertyId, QQmlDMAbstractItemModelData::get_property)); QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate(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(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(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 &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(&builder, this); const QByteArray propertyType = QByteArrayLiteral("QVariant"); const QAbstractItemModel *aim = model.aim(); const QHash names = aim ? aim->roleNames() : QHash(); for (QHash::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(this) = *metaObject; propertyCache = QQmlPropertyCache::createStandalone( metaObject.data(), model.modelItemRevision); } QV4::PersistentValue prototype; QList propertyRoles; QList watchedRoleIds; QList watchedRoles; QHash roleNames; QQmlAdaptorModel *model; int propertyOffset; int signalOffset; }; QT_END_NAMESPACE #endif // QQMLDMABSTRACTITEMMODELDATA_P_H