1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQMLDMOBJECTDATA_P_H
5#define QQMLDMOBJECTDATA_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qqmladaptormodelenginedata_p.h>
19#include <private/qqmldelegatemodel_p_p.h>
20
21#include <private/qobject_p.h>
22#include <QtCore/qpointer.h>
23
24QT_BEGIN_NAMESPACE
25
26class VDMObjectDelegateDataType;
27class QQmlDMObjectData : public QQmlDelegateModelItem
28{
29 Q_OBJECT
30 Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged)
31 QT_ANONYMOUS_PROPERTY(QObject * READ modelData NOTIFY modelDataChanged FINAL)
32public:
33 QQmlDMObjectData(
34 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
35 VDMObjectDelegateDataType *dataType,
36 int index, int row, int column,
37 QObject *object);
38
39 void setModelData(QObject *modelData)
40 {
41 if (modelData == object)
42 return;
43
44 object = modelData;
45 emit modelDataChanged();
46 }
47
48 QObject *modelData() const { return object; }
49 QQmlRefPointer<QQmlContextData> initProxy() final;
50
51 QPointer<QObject> object;
52
53Q_SIGNALS:
54 void modelDataChanged();
55};
56
57class VDMObjectDelegateDataType final
58 : public QQmlRefCounted<VDMObjectDelegateDataType>,
59 public QQmlAdaptorModel::Accessors
60{
61public:
62 QMetaObjectBuilder builder;
63 QQmlAdaptorModel *model = nullptr;
64 int propertyOffset = 0;
65 int signalOffset = 0;
66 bool shared = false;
67
68 VDMObjectDelegateDataType(QQmlAdaptorModel *model)
69 : model(model)
70 , shared(true)
71 {
72 }
73
74 VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type)
75 : builder(type.metaObject.data(), QMetaObjectBuilder::Properties
76 | QMetaObjectBuilder::Signals
77 | QMetaObjectBuilder::SuperClass
78 | QMetaObjectBuilder::ClassName)
79 , model(type.model)
80 , propertyOffset(type.propertyOffset)
81 , signalOffset(type.signalOffset)
82 {
83 builder.setFlags(MetaObjectFlag::DynamicMetaObject);
84 }
85
86 int rowCount(const QQmlAdaptorModel &model) const override
87 {
88 return model.list.count();
89 }
90
91 int columnCount(const QQmlAdaptorModel &) const override
92 {
93 return 1;
94 }
95
96 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
97 {
98 if (QObject *object = model.list.at(index).value<QObject *>())
99 return object->property(name: role.toUtf8());
100 return QVariant();
101 }
102
103 QQmlDelegateModelItem *createItem(
104 QQmlAdaptorModel &model,
105 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
106 int index, int row, int column) override
107 {
108 if (!metaObject)
109 initializeMetaObject();
110 return index >= 0 && index < model.list.count()
111 ? new QQmlDMObjectData(metaType, this, index, row, column, qvariant_cast<QObject *>(v: model.list.at(index)))
112 : nullptr;
113 }
114
115 void initializeMetaObject()
116 {
117 QQmlAdaptorModelEngineData::setModelDataType<QQmlDMObjectData>(builder: &builder, metaType: this);
118
119 metaObject.reset(other: builder.toMetaObject());
120 // Note: ATM we cannot create a shared property cache for this class, since each model
121 // object can have different properties. And to make those properties available to the
122 // delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass
123 // (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache.
124 // By not having a shared property cache, revisioned properties in QQmlDelegateModelItem
125 // will always be available to the delegate, regardless of the import version.
126 }
127
128 void cleanup(QQmlAdaptorModel &) const override
129 {
130 release();
131 }
132
133 bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
134 {
135 for (auto modelItem : items) {
136 const int modelItemIndex = modelItem->modelIndex();
137 if (modelItemIndex < index || modelItemIndex >= index + count)
138 continue;
139
140 auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem);
141 QObject *updatedModelData = qvariant_cast<QObject *>(
142 v: model.list.at(objectModelItem->modelIndex()));
143 objectModelItem->setModelData(updatedModelData);
144 }
145 return true;
146 }
147};
148
149class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
150{
151public:
152 QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
153 : m_data(data)
154 , m_type(type)
155 {
156 QObjectPrivate *op = QObjectPrivate::get(o: m_data);
157 *static_cast<QMetaObject *>(this) = *type->metaObject;
158 op->metaObject = this;
159 m_type->addref();
160 }
161
162 ~QQmlDMObjectDataMetaObject()
163 {
164 m_type->release();
165 }
166
167 int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override
168 {
169 Q_ASSERT(o == m_data);
170 Q_UNUSED(o);
171
172 static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
173 if (id >= m_type->propertyOffset
174 && (call == QMetaObject::ReadProperty
175 || call == QMetaObject::WriteProperty
176 || call == QMetaObject::ResetProperty)) {
177 if (m_data->object)
178 QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments);
179 return -1;
180 } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
181 QMetaObject::activate(sender: m_data, this, local_signal_index: id - m_type->signalOffset, argv: nullptr);
182 return -1;
183 } else {
184 return m_data->qt_metacall(call, id, arguments);
185 }
186 }
187
188 int createProperty(const char *name, const char *) override
189 {
190 if (!m_data->object)
191 return -1;
192 const QMetaObject *metaObject = m_data->object->metaObject();
193 static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
194
195 const int previousPropertyCount = propertyCount() - propertyOffset();
196 int propertyIndex = metaObject->indexOfProperty(name);
197 if (propertyIndex == -1)
198 return -1;
199 if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount())
200 return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
201
202 if (m_type->shared) {
203 VDMObjectDelegateDataType *type = m_type;
204 m_type = new VDMObjectDelegateDataType(*m_type);
205 type->release();
206 }
207
208 const int previousMethodCount = methodCount();
209 int notifierId = previousMethodCount - methodOffset();
210 for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) {
211 QMetaProperty property = metaObject->property(index: propertyId + objectPropertyOffset);
212 QMetaPropertyBuilder propertyBuilder;
213 if (property.hasNotifySignal()) {
214 m_type->builder.addSignal(signature: "__" + QByteArray::number(propertyId) + "()");
215 propertyBuilder = m_type->builder.addProperty(name: property.name(), type: property.typeName(), notifierId);
216 ++notifierId;
217 } else {
218 propertyBuilder = m_type->builder.addProperty(name: property.name(), type: property.typeName());
219 }
220 const bool modelWritable = m_type->model->delegateModelAccess != QQmlDelegateModel::ReadOnly;
221 propertyBuilder.setWritable(modelWritable && property.isWritable());
222 propertyBuilder.setResettable(modelWritable && property.isResettable());
223 propertyBuilder.setConstant(property.isConstant());
224 }
225
226 m_type->metaObject.reset(other: m_type->builder.toMetaObject());
227 *static_cast<QMetaObject *>(this) = *m_type->metaObject;
228
229 notifierId = previousMethodCount;
230 for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) {
231 QMetaProperty property = metaObject->property(index: i + objectPropertyOffset);
232 if (property.hasNotifySignal()) {
233 QQmlPropertyPrivate::connect(
234 sender: m_data->object, signal_index: property.notifySignalIndex(), receiver: m_data, method_index: notifierId);
235 ++notifierId;
236 }
237 }
238 return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
239 }
240
241 QQmlDMObjectData *m_data;
242 VDMObjectDelegateDataType *m_type;
243};
244
245QT_END_NAMESPACE
246
247#endif // QQMLDMOBJECTDATA_P_H
248

source code of qtdeclarative/src/qmlmodels/qqmldmobjectdata_p.h