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 QQMLDMABSTRACTITEMMODELDATA_P_H |
5 | #define QQMLDMABSTRACTITEMMODELDATA_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 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | class VDMAbstractItemModelDataType; |
25 | class QQmlDMAbstractItemModelData : public QQmlDelegateModelItem |
26 | { |
27 | Q_OBJECT |
28 | Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) |
29 | Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) |
30 | QT_ANONYMOUS_PROPERTY(QVariant READ modelData NOTIFY modelDataChanged FINAL) |
31 | |
32 | public: |
33 | QQmlDMAbstractItemModelData( |
34 | const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType, |
35 | VDMAbstractItemModelDataType *dataType, |
36 | int index, int row, int column); |
37 | |
38 | int metaCall(QMetaObject::Call call, int id, void **arguments); |
39 | bool hasModelChildren() const; |
40 | |
41 | QV4::ReturnedValue get() override; |
42 | void setValue(const QString &role, const QVariant &value) override; |
43 | bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; |
44 | |
45 | static QV4::ReturnedValue get_property( |
46 | const QV4::FunctionObject *b, const QV4::Value *thisObject, |
47 | const QV4::Value *argv, int argc); |
48 | static QV4::ReturnedValue set_property( |
49 | const QV4::FunctionObject *b, const QV4::Value *thisObject, |
50 | const QV4::Value *argv, int argc); |
51 | |
52 | static QV4::ReturnedValue get_modelData( |
53 | const QV4::FunctionObject *b, const QV4::Value *thisObject, |
54 | const QV4::Value *argv, int argc); |
55 | static QV4::ReturnedValue set_modelData( |
56 | const QV4::FunctionObject *b, const QV4::Value *thisObject, |
57 | const QV4::Value *argv, int argc); |
58 | |
59 | QVariant modelData() const; |
60 | void setModelData(const QVariant &modelData); |
61 | |
62 | const VDMAbstractItemModelDataType *type() const { return m_type; } |
63 | |
64 | Q_SIGNALS: |
65 | void modelDataChanged(); |
66 | |
67 | private: |
68 | QVariant value(int role) const; |
69 | void setValue(int role, const QVariant &value); |
70 | |
71 | VDMAbstractItemModelDataType *m_type; |
72 | QVector<QVariant> m_cachedData; |
73 | }; |
74 | |
75 | class VDMAbstractItemModelDataType final |
76 | : public QQmlRefCounted<VDMAbstractItemModelDataType> |
77 | , public QQmlAdaptorModel::Accessors |
78 | , public QAbstractDynamicMetaObject |
79 | { |
80 | public: |
81 | VDMAbstractItemModelDataType(QQmlAdaptorModel *model) |
82 | : model(model) |
83 | , propertyOffset(0) |
84 | , signalOffset(0) |
85 | { |
86 | } |
87 | |
88 | void notifyItem(const QQmlGuard<QQmlDMAbstractItemModelData> &item, const QVector<int> &signalIndexes) const |
89 | { |
90 | for (const int signalIndex : signalIndexes) { |
91 | QMetaObject::activate(sender: item, signal_index: signalIndex, argv: nullptr); |
92 | if (item.isNull()) |
93 | return; |
94 | } |
95 | emit item->modelDataChanged(); |
96 | } |
97 | |
98 | bool notify( |
99 | const QQmlAdaptorModel &, |
100 | const QList<QQmlDelegateModelItem *> &items, |
101 | int index, |
102 | int count, |
103 | const QVector<int> &roles) const override |
104 | { |
105 | bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); |
106 | if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { |
107 | QList<int> roleIds; |
108 | for (const QByteArray &r : watchedRoles) { |
109 | QHash<QByteArray, int>::const_iterator it = roleNames.find(key: r); |
110 | if (it != roleNames.end()) |
111 | roleIds << it.value(); |
112 | } |
113 | const_cast<VDMAbstractItemModelDataType *>(this)->watchedRoleIds = roleIds; |
114 | } |
115 | |
116 | QVector<int> signalIndexes; |
117 | for (int i = 0; i < roles.size(); ++i) { |
118 | const int role = roles.at(i); |
119 | if (!changed && watchedRoleIds.contains(t: role)) |
120 | changed = true; |
121 | |
122 | int propertyId = propertyRoles.indexOf(t: role); |
123 | if (propertyId != -1) |
124 | signalIndexes.append(t: propertyId + signalOffset); |
125 | } |
126 | if (roles.isEmpty()) { |
127 | const int propertyRolesCount = propertyRoles.size(); |
128 | signalIndexes.reserve(asize: propertyRolesCount); |
129 | for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId) |
130 | signalIndexes.append(t: propertyId + signalOffset); |
131 | } |
132 | |
133 | QVarLengthArray<QQmlGuard<QQmlDMAbstractItemModelData>> guardedItems; |
134 | for (const auto item : items) { |
135 | Q_ASSERT(qobject_cast<QQmlDMAbstractItemModelData *>(item) == item); |
136 | guardedItems.append(t: static_cast<QQmlDMAbstractItemModelData *>(item)); |
137 | } |
138 | |
139 | for (const auto &item : std::as_const(t&: guardedItems)) { |
140 | if (item.isNull()) |
141 | continue; |
142 | |
143 | const int idx = item->modelIndex(); |
144 | if (idx >= index && idx < index + count) |
145 | notifyItem(item, signalIndexes); |
146 | } |
147 | return changed; |
148 | } |
149 | |
150 | void replaceWatchedRoles( |
151 | QQmlAdaptorModel &, |
152 | const QList<QByteArray> &oldRoles, |
153 | const QList<QByteArray> &newRoles) const override |
154 | { |
155 | VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); |
156 | |
157 | dataType->watchedRoleIds.clear(); |
158 | for (const QByteArray &oldRole : oldRoles) |
159 | dataType->watchedRoles.removeOne(t: oldRole); |
160 | dataType->watchedRoles += newRoles; |
161 | } |
162 | |
163 | static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) |
164 | { |
165 | QV4::Scope scope(b); |
166 | QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); |
167 | if (!o) |
168 | RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"))); |
169 | |
170 | const QQmlAdaptorModel *const model |
171 | = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->type()->model; |
172 | if (o->d()->item->index >= 0) { |
173 | if (const QAbstractItemModel *const aim = model->aim()) |
174 | RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex)))); |
175 | } |
176 | RETURN_RESULT(QV4::Encode(false)); |
177 | } |
178 | |
179 | |
180 | void initializeConstructor(QQmlAdaptorModelEngineData *const data) |
181 | { |
182 | QV4::ExecutionEngine *v4 = data->v4; |
183 | QV4::Scope scope(v4); |
184 | QV4::ScopedObject proto(scope, v4->newObject()); |
185 | proto->defineAccessorProperty(QStringLiteral("index"), getter: QQmlAdaptorModelEngineData::get_index, setter: nullptr); |
186 | proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), getter: get_hasModelChildren, setter: nullptr); |
187 | proto->defineAccessorProperty(QStringLiteral("modelData"), |
188 | getter: QQmlDMAbstractItemModelData::get_modelData, |
189 | setter: QQmlDMAbstractItemModelData::set_modelData); |
190 | QV4::ScopedProperty p(scope); |
191 | |
192 | typedef QHash<QByteArray, int>::const_iterator iterator; |
193 | for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) { |
194 | const qsizetype propertyId = propertyRoles.indexOf(t: it.value()); |
195 | const QByteArray &propertyName = it.key(); |
196 | |
197 | QV4::ScopedString name(scope, v4->newString(s: QString::fromUtf8(ba: propertyName))); |
198 | QV4::ScopedFunctionObject g( |
199 | scope, |
200 | v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>( |
201 | args&: v4, args: propertyId, args&: QQmlDMAbstractItemModelData::get_property)); |
202 | QV4::ScopedFunctionObject s( |
203 | scope, |
204 | v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>( |
205 | args&: v4, args: propertyId, args&: QQmlDMAbstractItemModelData::set_property)); |
206 | p->setGetter(g); |
207 | p->setSetter(s); |
208 | proto->insertMember(s: name, p, attributes: QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); |
209 | } |
210 | prototype.set(engine: v4, value: proto); |
211 | } |
212 | |
213 | // QAbstractDynamicMetaObject |
214 | |
215 | void objectDestroyed(QObject *) override |
216 | { |
217 | release(); |
218 | } |
219 | |
220 | int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override |
221 | { |
222 | return static_cast<QQmlDMAbstractItemModelData *>(object)->metaCall(call, id, arguments); |
223 | } |
224 | |
225 | int rowCount(const QQmlAdaptorModel &model) const override |
226 | { |
227 | if (const QAbstractItemModel *aim = model.aim()) |
228 | return aim->rowCount(parent: model.rootIndex); |
229 | return 0; |
230 | } |
231 | |
232 | int columnCount(const QQmlAdaptorModel &model) const override |
233 | { |
234 | if (const QAbstractItemModel *aim = model.aim()) |
235 | return aim->columnCount(parent: model.rootIndex); |
236 | return 0; |
237 | } |
238 | |
239 | void cleanup(QQmlAdaptorModel &) const override |
240 | { |
241 | release(); |
242 | } |
243 | |
244 | QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override |
245 | { |
246 | if (!metaObject) { |
247 | VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); |
248 | dataType->initializeMetaType(model); |
249 | } |
250 | |
251 | if (const QAbstractItemModel *aim = model.aim()) { |
252 | const QModelIndex modelIndex |
253 | = aim->index(row: model.rowAt(index), column: model.columnAt(index), parent: model.rootIndex); |
254 | |
255 | const auto it = roleNames.find(key: role.toUtf8()), end = roleNames.end(); |
256 | if (it != roleNames.end()) |
257 | return modelIndex.data(arole: *it); |
258 | |
259 | if (role.isEmpty() || role == QLatin1String("modelData")) { |
260 | if (roleNames.size() == 1) |
261 | return modelIndex.data(arole: roleNames.begin().value()); |
262 | |
263 | QVariantMap modelData; |
264 | for (auto jt = roleNames.begin(); jt != end; ++jt) |
265 | modelData.insert(key: QString::fromUtf8(ba: jt.key()), value: modelIndex.data(arole: jt.value())); |
266 | return modelData; |
267 | } |
268 | |
269 | if (role == QLatin1String("hasModelChildren")) |
270 | return QVariant(aim->hasChildren(parent: modelIndex)); |
271 | } |
272 | return QVariant(); |
273 | } |
274 | |
275 | QVariant parentModelIndex(const QQmlAdaptorModel &model) const override |
276 | { |
277 | if (const QAbstractItemModel *aim = model.aim()) |
278 | return QVariant::fromValue(value: aim->parent(child: model.rootIndex)); |
279 | return QVariant(); |
280 | } |
281 | |
282 | QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override |
283 | { |
284 | if (const QAbstractItemModel *aim = model.aim()) |
285 | return QVariant::fromValue(value: aim->index(row: model.rowAt(index), column: model.columnAt(index), |
286 | parent: model.rootIndex)); |
287 | return QVariant(); |
288 | } |
289 | |
290 | bool canFetchMore(const QQmlAdaptorModel &model) const override |
291 | { |
292 | if (const QAbstractItemModel *aim = model.aim()) |
293 | return aim->canFetchMore(parent: model.rootIndex); |
294 | return false; |
295 | } |
296 | |
297 | void fetchMore(QQmlAdaptorModel &model) const override |
298 | { |
299 | if (QAbstractItemModel *aim = model.aim()) |
300 | aim->fetchMore(parent: model.rootIndex); |
301 | } |
302 | |
303 | QQmlDelegateModelItem *createItem( |
304 | QQmlAdaptorModel &model, |
305 | const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType, |
306 | int index, int row, int column) override |
307 | { |
308 | if (!metaObject) |
309 | initializeMetaType(model); |
310 | return new QQmlDMAbstractItemModelData(metaType, this, index, row, column); |
311 | } |
312 | |
313 | void initializeMetaType(const QQmlAdaptorModel &model) |
314 | { |
315 | QMetaObjectBuilder builder; |
316 | QQmlAdaptorModelEngineData::setModelDataType<QQmlDMAbstractItemModelData>(builder: &builder, metaType: this); |
317 | |
318 | const QByteArray propertyType = QByteArrayLiteral("QVariant"); |
319 | const QAbstractItemModel *aim = model.aim(); |
320 | const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>(); |
321 | for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) { |
322 | const int propertyId = propertyRoles.size(); |
323 | propertyRoles.append(t: it.key()); |
324 | roleNames.insert(key: it.value(), value: it.key()); |
325 | QQmlAdaptorModelEngineData::addProperty(builder: &builder, propertyId, propertyName: it.value(), propertyType); |
326 | } |
327 | |
328 | metaObject.reset(other: builder.toMetaObject()); |
329 | *static_cast<QMetaObject *>(this) = *metaObject; |
330 | propertyCache = QQmlPropertyCache::createStandalone( |
331 | metaObject.data(), metaObjectRevision: model.modelItemRevision); |
332 | } |
333 | |
334 | QV4::PersistentValue prototype; |
335 | QList<int> propertyRoles; |
336 | QList<int> watchedRoleIds; |
337 | QList<QByteArray> watchedRoles; |
338 | QHash<QByteArray, int> roleNames; |
339 | QQmlAdaptorModel *model; |
340 | int propertyOffset; |
341 | int signalOffset; |
342 | }; |
343 | |
344 | QT_END_NAMESPACE |
345 | |
346 | #endif // QQMLDMABSTRACTITEMMODELDATA_P_H |
347 |
Definitions
Learn Advanced QML with KDAB
Find out more