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#include <private/qobject_p.h>
21
22QT_BEGIN_NAMESPACE
23
24class VDMObjectDelegateDataType;
25class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface
26{
27 Q_OBJECT
28 Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged FINAL)
29 QT_ANONYMOUS_PROPERTY(QObject * READ modelData NOTIFY modelDataChanged)
30 Q_INTERFACES(QQmlAdaptorModelProxyInterface)
31public:
32 QQmlDMObjectData(
33 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
34 VDMObjectDelegateDataType *dataType,
35 int index, int row, int column,
36 QObject *object);
37
38 void setModelData(QObject *modelData)
39 {
40 if (modelData == object)
41 return;
42
43 object = modelData;
44 emit modelDataChanged();
45 }
46
47 QObject *modelData() const { return object; }
48 QObject *proxiedObject() override { return object; }
49
50 QPointer<QObject> object;
51
52Q_SIGNALS:
53 void modelDataChanged();
54};
55
56class VDMObjectDelegateDataType
57 : public QQmlRefCounted<VDMObjectDelegateDataType>,
58 public QQmlAdaptorModel::Accessors
59{
60public:
61 int propertyOffset;
62 int signalOffset;
63 bool shared;
64 QMetaObjectBuilder builder;
65
66 VDMObjectDelegateDataType()
67 : propertyOffset(0)
68 , signalOffset(0)
69 , shared(true)
70 {
71 }
72
73 VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type)
74 : propertyOffset(type.propertyOffset)
75 , signalOffset(type.signalOffset)
76 , shared(false)
77 , builder(type.metaObject.data(), QMetaObjectBuilder::Properties
78 | QMetaObjectBuilder::Signals
79 | QMetaObjectBuilder::SuperClass
80 | QMetaObjectBuilder::ClassName)
81 {
82 builder.setFlags(MetaObjectFlag::DynamicMetaObject);
83 }
84
85 int rowCount(const QQmlAdaptorModel &model) const override
86 {
87 return model.list.count();
88 }
89
90 int columnCount(const QQmlAdaptorModel &) const override
91 {
92 return 1;
93 }
94
95 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
96 {
97 if (QObject *object = model.list.at(index).value<QObject *>())
98 return object->property(name: role.toUtf8());
99 return QVariant();
100 }
101
102 QQmlDelegateModelItem *createItem(
103 QQmlAdaptorModel &model,
104 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
105 int index, int row, int column) override
106 {
107 if (!metaObject)
108 initializeMetaType(model);
109 return index >= 0 && index < model.list.count()
110 ? new QQmlDMObjectData(metaType, this, index, row, column, qvariant_cast<QObject *>(v: model.list.at(index)))
111 : nullptr;
112 }
113
114 void initializeMetaType(QQmlAdaptorModel &model)
115 {
116 Q_UNUSED(model);
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->index;
137 if (modelItemIndex < index || modelItemIndex >= index + count)
138 continue;
139
140 auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem);
141 QObject *updatedModelData = qvariant_cast<QObject *>(v: model.list.at(objectModelItem->index));
142 objectModelItem->setModelData(updatedModelData);
143 }
144 return true;
145 }
146};
147
148class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
149{
150public:
151 QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
152 : m_data(data)
153 , m_type(type)
154 {
155 QObjectPrivate *op = QObjectPrivate::get(o: m_data);
156 *static_cast<QMetaObject *>(this) = *type->metaObject;
157 op->metaObject = this;
158 m_type->addref();
159 }
160
161 ~QQmlDMObjectDataMetaObject()
162 {
163 m_type->release();
164 }
165
166 int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override
167 {
168 Q_ASSERT(o == m_data);
169 Q_UNUSED(o);
170
171 static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
172 if (id >= m_type->propertyOffset
173 && (call == QMetaObject::ReadProperty
174 || call == QMetaObject::WriteProperty
175 || call == QMetaObject::ResetProperty)) {
176 if (m_data->object)
177 QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments);
178 return -1;
179 } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
180 QMetaObject::activate(sender: m_data, this, local_signal_index: id - m_type->signalOffset, argv: nullptr);
181 return -1;
182 } else {
183 return m_data->qt_metacall(call, id, arguments);
184 }
185 }
186
187 int createProperty(const char *name, const char *) override
188 {
189 if (!m_data->object)
190 return -1;
191 const QMetaObject *metaObject = m_data->object->metaObject();
192 static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
193
194 const int previousPropertyCount = propertyCount() - propertyOffset();
195 int propertyIndex = metaObject->indexOfProperty(name);
196 if (propertyIndex == -1)
197 return -1;
198 if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount())
199 return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
200
201 if (m_type->shared) {
202 VDMObjectDelegateDataType *type = m_type;
203 m_type = new VDMObjectDelegateDataType(*m_type);
204 type->release();
205 }
206
207 const int previousMethodCount = methodCount();
208 int notifierId = previousMethodCount - methodOffset();
209 for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) {
210 QMetaProperty property = metaObject->property(index: propertyId + objectPropertyOffset);
211 QMetaPropertyBuilder propertyBuilder;
212 if (property.hasNotifySignal()) {
213 m_type->builder.addSignal(signature: "__" + QByteArray::number(propertyId) + "()");
214 propertyBuilder = m_type->builder.addProperty(name: property.name(), type: property.typeName(), notifierId);
215 ++notifierId;
216 } else {
217 propertyBuilder = m_type->builder.addProperty(name: property.name(), type: property.typeName());
218 }
219 propertyBuilder.setWritable(property.isWritable());
220 propertyBuilder.setResettable(property.isResettable());
221 propertyBuilder.setConstant(property.isConstant());
222 }
223
224 m_type->metaObject.reset(other: m_type->builder.toMetaObject());
225 *static_cast<QMetaObject *>(this) = *m_type->metaObject;
226
227 notifierId = previousMethodCount;
228 for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) {
229 QMetaProperty property = metaObject->property(index: i + objectPropertyOffset);
230 if (property.hasNotifySignal()) {
231 QQmlPropertyPrivate::connect(
232 sender: m_data->object, signal_index: property.notifySignalIndex(), receiver: m_data, method_index: notifierId);
233 ++notifierId;
234 }
235 }
236 return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
237 }
238
239 QQmlDMObjectData *m_data;
240 VDMObjectDelegateDataType *m_type;
241};
242
243QT_END_NAMESPACE
244
245#endif // QQMLDMOBJECTDATA_P_H
246

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