// 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 QQMLDMOBJECTDATA_P_H #define QQMLDMOBJECTDATA_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 #include QT_BEGIN_NAMESPACE class VDMObjectDelegateDataType; class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface { Q_OBJECT Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged) QT_ANONYMOUS_PROPERTY(QObject * READ modelData NOTIFY modelDataChanged FINAL) Q_INTERFACES(QQmlAdaptorModelProxyInterface) public: QQmlDMObjectData( const QQmlRefPointer &metaType, VDMObjectDelegateDataType *dataType, int index, int row, int column, QObject *object); void setModelData(QObject *modelData) { if (modelData == object) return; object = modelData; emit modelDataChanged(); } QObject *modelData() const { return object; } QObject *proxiedObject() override { return object; } QPointer object; Q_SIGNALS: void modelDataChanged(); }; class VDMObjectDelegateDataType final : public QQmlRefCounted, public QQmlAdaptorModel::Accessors { public: int propertyOffset; int signalOffset; bool shared; QMetaObjectBuilder builder; VDMObjectDelegateDataType() : propertyOffset(0) , signalOffset(0) , shared(true) { } VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type) : propertyOffset(type.propertyOffset) , signalOffset(type.signalOffset) , shared(false) , builder(type.metaObject.data(), QMetaObjectBuilder::Properties | QMetaObjectBuilder::Signals | QMetaObjectBuilder::SuperClass | QMetaObjectBuilder::ClassName) { builder.setFlags(MetaObjectFlag::DynamicMetaObject); } int rowCount(const QQmlAdaptorModel &model) const override { return model.list.count(); } int columnCount(const QQmlAdaptorModel &) const override { return 1; } QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { if (QObject *object = model.list.at(index).value()) return object->property(role.toUtf8()); return QVariant(); } QQmlDelegateModelItem *createItem( QQmlAdaptorModel &model, const QQmlRefPointer &metaType, int index, int row, int column) override { if (!metaObject) initializeMetaType(model); return index >= 0 && index < model.list.count() ? new QQmlDMObjectData(metaType, this, index, row, column, qvariant_cast(model.list.at(index))) : nullptr; } void initializeMetaType(QQmlAdaptorModel &model) { Q_UNUSED(model); QQmlAdaptorModelEngineData::setModelDataType(&builder, this); metaObject.reset(builder.toMetaObject()); // Note: ATM we cannot create a shared property cache for this class, since each model // object can have different properties. And to make those properties available to the // delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass // (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache. // By not having a shared property cache, revisioned properties in QQmlDelegateModelItem // will always be available to the delegate, regardless of the import version. } void cleanup(QQmlAdaptorModel &) const override { release(); } bool notify(const QQmlAdaptorModel &model, const QList &items, int index, int count, const QVector &) const override { for (auto modelItem : items) { const int modelItemIndex = modelItem->index; if (modelItemIndex < index || modelItemIndex >= index + count) continue; auto objectModelItem = static_cast(modelItem); QObject *updatedModelData = qvariant_cast(model.list.at(objectModelItem->index)); objectModelItem->setModelData(updatedModelData); } return true; } }; class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject { public: QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type) : m_data(data) , m_type(type) { QObjectPrivate *op = QObjectPrivate::get(m_data); *static_cast(this) = *type->metaObject; op->metaObject = this; m_type->addref(); } ~QQmlDMObjectDataMetaObject() { m_type->release(); } int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override { Q_ASSERT(o == m_data); Q_UNUSED(o); static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); if (id >= m_type->propertyOffset && (call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty || call == QMetaObject::ResetProperty)) { if (m_data->object) QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments); return -1; } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) { QMetaObject::activate(m_data, this, id - m_type->signalOffset, nullptr); return -1; } else { return m_data->qt_metacall(call, id, arguments); } } int createProperty(const char *name, const char *) override { if (!m_data->object) return -1; const QMetaObject *metaObject = m_data->object->metaObject(); static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); const int previousPropertyCount = propertyCount() - propertyOffset(); int propertyIndex = metaObject->indexOfProperty(name); if (propertyIndex == -1) return -1; if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount()) return propertyIndex + m_type->propertyOffset - objectPropertyOffset; if (m_type->shared) { VDMObjectDelegateDataType *type = m_type; m_type = new VDMObjectDelegateDataType(*m_type); type->release(); } const int previousMethodCount = methodCount(); int notifierId = previousMethodCount - methodOffset(); for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) { QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset); QMetaPropertyBuilder propertyBuilder; if (property.hasNotifySignal()) { m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId); ++notifierId; } else { propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName()); } propertyBuilder.setWritable(property.isWritable()); propertyBuilder.setResettable(property.isResettable()); propertyBuilder.setConstant(property.isConstant()); } m_type->metaObject.reset(m_type->builder.toMetaObject()); *static_cast(this) = *m_type->metaObject; notifierId = previousMethodCount; for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) { QMetaProperty property = metaObject->property(i + objectPropertyOffset); if (property.hasNotifySignal()) { QQmlPropertyPrivate::connect( m_data->object, property.notifySignalIndex(), m_data, notifierId); ++notifierId; } } return propertyIndex + m_type->propertyOffset - objectPropertyOffset; } QQmlDMObjectData *m_data; VDMObjectDelegateDataType *m_type; }; QT_END_NAMESPACE #endif // QQMLDMOBJECTDATA_P_H