1// Copyright (C) 2020 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 QQMLLISTMODEL_P_P_H
5#define QQMLLISTMODEL_P_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 "qqmllistmodel_p.h"
19#include <private/qtqmlmodelsglobal_p.h>
20#include <private/qqmlengine_p.h>
21#include <private/qqmlopenmetaobject_p.h>
22#include <private/qv4qobjectwrapper_p.h>
23#include <qqml.h>
24
25QT_REQUIRE_CONFIG(qml_list_model);
26
27QT_BEGIN_NAMESPACE
28
29
30class DynamicRoleModelNode;
31
32class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject
33{
34public:
35 DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object);
36 ~DynamicRoleModelNodeMetaObject();
37
38 bool m_enabled;
39
40protected:
41 void propertyWrite(int index) override;
42 void propertyWritten(int index) override;
43
44private:
45 DynamicRoleModelNode *m_owner;
46};
47
48class DynamicRoleModelNode : public QObject
49{
50 Q_OBJECT
51public:
52 DynamicRoleModelNode(QQmlListModel *owner, int uid);
53
54 static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner);
55
56 void updateValues(const QVariantMap &object, QVector<int> &roles);
57
58 QVariant getValue(const QString &name) const
59 {
60 return m_meta->value(name.toUtf8());
61 }
62
63 bool setValue(const QByteArray &name, const QVariant &val)
64 {
65 return m_meta->setValue(name, val);
66 }
67
68 void setNodeUpdatesEnabled(bool enable)
69 {
70 m_meta->m_enabled = enable;
71 }
72
73 int getUid() const
74 {
75 return m_uid;
76 }
77
78 static QVector<int> sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target);
79
80private:
81 QQmlListModel *m_owner;
82 int m_uid;
83 DynamicRoleModelNodeMetaObject *m_meta;
84
85 friend class DynamicRoleModelNodeMetaObject;
86};
87
88class ModelNodeMetaObject : public QQmlOpenMetaObject
89{
90public:
91 ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex);
92 ~ModelNodeMetaObject();
93
94#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
95 const QMetaObject *toDynamicMetaObject(QObject *object) const override;
96#else
97 QMetaObject *toDynamicMetaObject(QObject *object) override;
98#endif
99
100 static ModelNodeMetaObject *get(QObject *obj);
101
102 bool m_enabled;
103 QQmlListModel *m_model;
104 int m_elementIndex;
105
106 void updateValues();
107 void updateValues(const QVector<int> &roles);
108
109 bool initialized() const { return m_initialized; }
110
111protected:
112 void propertyWritten(int index) override;
113
114private:
115 using QQmlOpenMetaObject::setValue;
116
117 void emitDirectNotifies(const int *changedRoles, int roleCount);
118
119 void initialize();
120 mutable bool m_initialized;
121};
122
123namespace QV4 {
124
125namespace Heap {
126
127struct ModelObject : public QObjectWrapper {
128 void init(QObject *object, QQmlListModel *model)
129 {
130 QObjectWrapper::init(object);
131 m_model = model;
132 }
133
134 void destroy()
135 {
136 m_model.destroy();
137 QObjectWrapper::destroy();
138 }
139
140 int elementIndex() const {
141 if (const QObject *o = object()) {
142 const QObjectPrivate *op = QObjectPrivate::get(o);
143 return static_cast<ModelNodeMetaObject *>(op->metaObject)->m_elementIndex;
144 }
145 return -1;
146 }
147
148 QV4QPointer<QQmlListModel> m_model;
149};
150
151}
152
153struct ModelObject : public QObjectWrapper
154{
155 V4_OBJECT2(ModelObject, QObjectWrapper)
156 V4_NEEDS_DESTROY
157
158protected:
159 static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver);
160 static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
161 static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
162 static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
163 static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
164};
165
166} // namespace QV4
167
168class ListLayout
169{
170public:
171 ListLayout() : currentBlock(0), currentBlockOffset(0) {}
172 ListLayout(const ListLayout *other);
173 ~ListLayout();
174
175 class Role
176 {
177 public:
178
179 Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {}
180 explicit Role(const Role *other);
181 ~Role();
182
183 // This enum must be kept in sync with the roleTypeNames variable in qqmllistmodel.cpp
184 enum DataType
185 {
186 Invalid = -1,
187
188 String,
189 Number,
190 Bool,
191 List,
192 QObject,
193 VariantMap,
194 DateTime,
195 Url,
196 Function,
197
198 MaxDataType
199 };
200
201 QString name;
202 DataType type;
203 int blockIndex;
204 int blockOffset;
205 int index;
206 ListLayout *subLayout;
207 };
208
209 const Role *getRoleOrCreate(const QString &key, const QVariant &data);
210 const Role &getRoleOrCreate(QV4::String *key, Role::DataType type);
211 const Role &getRoleOrCreate(const QString &key, Role::DataType type);
212
213 const Role &getExistingRole(int index) const { return *roles.at(i: index); }
214 const Role *getExistingRole(const QString &key) const;
215 const Role *getExistingRole(QV4::String *key) const;
216
217 int roleCount() const { return roles.size(); }
218
219 static void sync(ListLayout *src, ListLayout *target);
220
221private:
222 const Role &createRole(const QString &key, Role::DataType type);
223
224 int currentBlock;
225 int currentBlockOffset;
226 QVector<Role *> roles;
227 QStringHash<Role *> roleHash;
228};
229
230struct StringOrTranslation
231{
232 ~StringOrTranslation();
233 bool isSet() const { return binding || arrayData; }
234 bool isTranslation() const { return binding && !arrayData; }
235 void setString(const QString &s);
236 void setTranslation(const QV4::CompiledData::Binding *binding);
237 QString toString(const QQmlListModel *owner) const;
238 QString asString() const;
239private:
240 void clear();
241
242 union {
243 char16_t *stringData = nullptr;
244 const QV4::CompiledData::Binding *binding;
245 };
246
247 QTypedArrayData<char16_t> *arrayData = nullptr;
248 uint stringSize = 0;
249};
250
251/*!
252\internal
253*/
254class ListElement
255{
256public:
257 enum ObjectIndestructible { Indestructible = 1, ExplicitlySet = 2 };
258 enum { BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) };
259
260 ListElement();
261 ListElement(int existingUid);
262 ~ListElement();
263
264 static QVector<int> sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout);
265
266private:
267
268 void destroy(ListLayout *layout);
269
270 int setVariantProperty(const ListLayout::Role &role, const QVariant &d);
271
272 int setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng);
273
274 int setStringProperty(const ListLayout::Role &role, const QString &s);
275 int setDoubleProperty(const ListLayout::Role &role, double n);
276 int setBoolProperty(const ListLayout::Role &role, bool b);
277 int setListProperty(const ListLayout::Role &role, ListModel *m);
278 int setQObjectProperty(const ListLayout::Role &role, QV4::QObjectWrapper *o);
279 int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o);
280 int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
281 int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
282 int setUrlProperty(const ListLayout::Role &role, const QUrl &url);
283 int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f);
284 int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b);
285
286 void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
287 void setDoublePropertyFast(const ListLayout::Role &role, double n);
288 void setBoolPropertyFast(const ListLayout::Role &role, bool b);
289 void setQObjectPropertyFast(const ListLayout::Role &role, QV4::QObjectWrapper *o);
290 void setListPropertyFast(const ListLayout::Role &role, ListModel *m);
291 void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o);
292 void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt);
293 void setUrlPropertyFast(const ListLayout::Role &role, const QUrl &url);
294 void setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f);
295
296 void clearProperty(const ListLayout::Role &role);
297
298 QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
299 ListModel *getListProperty(const ListLayout::Role &role);
300 StringOrTranslation *getStringProperty(const ListLayout::Role &role);
301 QV4::QObjectWrapper *getQObjectProperty(const ListLayout::Role &role);
302 QV4::PersistentValue *getGuardProperty(const ListLayout::Role &role);
303 QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
304 QDateTime *getDateTimeProperty(const ListLayout::Role &role);
305 QUrl *getUrlProperty(const ListLayout::Role &role);
306 QJSValue *getFunctionProperty(const ListLayout::Role &role);
307
308 inline char *getPropertyMemory(const ListLayout::Role &role);
309
310 int getUid() const { return uid; }
311
312 ModelNodeMetaObject *objectCache();
313
314 char data[BLOCK_SIZE];
315 ListElement *next;
316
317 int uid;
318 QObject *m_objectCache;
319
320 friend class ListModel;
321};
322
323/*!
324\internal
325*/
326class ListModel
327{
328public:
329
330 ListModel(ListLayout *layout, QQmlListModel *modelCache);
331
332 void destroy();
333
334 int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data);
335 int setExistingProperty(int uid, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng);
336
337 QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
338 ListModel *getListProperty(int elementIndex, const ListLayout::Role &role);
339
340 void updateTranslations();
341
342 int roleCount() const
343 {
344 return m_layout->roleCount();
345 }
346
347 const ListLayout::Role &getExistingRole(int index) const
348 {
349 return m_layout->getExistingRole(index);
350 }
351
352 const ListLayout::Role *getExistingRole(QV4::String *key) const
353 {
354 return m_layout->getExistingRole(key);
355 }
356
357 const ListLayout::Role &getOrCreateListRole(const QString &name)
358 {
359 return m_layout->getRoleOrCreate(key: name, type: ListLayout::Role::List);
360 }
361
362 int elementCount() const
363 {
364 return elements.count();
365 }
366
367 enum class SetElement {WasJustInserted, IsCurrentlyUpdated};
368
369 void set(int elementIndex, QV4::Object *object, QVector<int> *roles);
370 void set(int elementIndex, QV4::Object *object, SetElement reason = SetElement::IsCurrentlyUpdated);
371
372 int append(QV4::Object *object);
373 void insert(int elementIndex, QV4::Object *object);
374
375 Q_REQUIRED_RESULT QVector<std::function<void()>> remove(int index, int count);
376
377 int appendElement();
378 void insertElement(int index);
379
380 void move(int from, int to, int n);
381
382 static bool sync(ListModel *src, ListModel *target);
383
384 QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex);
385
386private:
387 QPODVector<ListElement *, 4> elements;
388 ListLayout *m_layout;
389
390 QQmlListModel *m_modelCache;
391
392 struct ElementSync
393 {
394 ListElement *src = nullptr;
395 ListElement *target = nullptr;
396 int srcIndex = -1;
397 int targetIndex = -1;
398 QVector<int> changedRoles;
399 };
400
401 void newElement(int index);
402
403 void updateCacheIndices(int start = 0, int end = -1);
404
405 template<typename ArrayLike>
406 ListModel *resolveSubModel(QV4::ScopedObject *o, const ListLayout::Role &r, ArrayLike *a)
407 {
408 ListModel *subModel = new ListModel(r.subLayout, nullptr);
409
410 for (qint64 j = 0, arrayLength = a->getLength(); j < arrayLength; ++j) {
411 *o = a->get(j);
412 subModel->append(object: *o);
413 }
414
415 return subModel;
416 }
417
418 template<typename ArrayLike>
419 void setArrayLikeFast(
420 QV4::ScopedObject *o, QV4::String *propertyName, ListElement *e, ArrayLike *a)
421 {
422 const ListLayout::Role &r = m_layout->getRoleOrCreate(key: propertyName, type: ListLayout::Role::List);
423 if (r.type == ListLayout::Role::List)
424 e->setListPropertyFast(role: r, m: resolveSubModel(o, r, a));
425 }
426
427 template<typename ArrayLike>
428 int setArrayLike(
429 QV4::ScopedObject *o, QV4::String *propertyName, ListElement *e, ArrayLike *a)
430 {
431 const ListLayout::Role &r = m_layout->getRoleOrCreate(key: propertyName, type: ListLayout::Role::List);
432 return e->setListProperty(role: r, m: resolveSubModel(o, r, a));
433 }
434
435 friend class ListElement;
436 friend class QQmlListModelWorkerAgent;
437 friend class QQmlListModelParser;
438};
439
440QT_END_NAMESPACE
441
442Q_DECLARE_METATYPE(ListModel *);
443
444#endif // QQUICKLISTMODEL_P_P_H
445

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