Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlobjectmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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// Qt-Security score:significant
4
6
7#include <QtQml/qqmlcontext.h>
8#include <QtQml/qqmlengine.h>
9#include <QtQml/qqmlinfo.h>
10
11#include <private/qqmlchangeset_p.h>
12#include <private/qqmlglobal_p.h>
13#include <private/qobject_p.h>
14#include <private/qv4qobjectwrapper_p.h>
15
16#include <QtCore/qcoreapplication.h>
17#include <QtCore/qhash.h>
18#include <QtCore/qlist.h>
19#include <QtCore/qvarlengtharray.h>
20
22
24{
25 Q_DECLARE_PUBLIC(QQmlObjectModel)
26public:
27 class Item {
28 public:
29 Item(QObject *i = nullptr) : m_item(i) {}
30
31 void addRef() { ++m_ref; }
32 bool deref() { return --m_ref == 0; }
33 int refCount() const { return m_ref; }
34
35 QObject *item() const { return m_item.data(); }
36
37 private:
39 int m_ref = 0;
40 };
41
43
44 static QQmlObjectModelPrivate *get(QQmlObjectModel *q) { return q->d_func(); }
45
46 static void children_append(QQmlListProperty<QObject> *prop, QObject *item) {
47 qsizetype index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.size();
48 static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item);
49 }
50
51 static qsizetype children_count(QQmlListProperty<QObject> *prop) {
52 return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.size();
53 }
54
55 static QObject *children_at(QQmlListProperty<QObject> *prop, qsizetype index) {
56 return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.at(index).item();
57 }
58
59 static void children_clear(QQmlListProperty<QObject> *prop) {
60 static_cast<QQmlObjectModelPrivate *>(prop->data)->clear();
61 }
62
63 static void children_replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *item) {
64 static_cast<QQmlObjectModelPrivate *>(prop->data)->replace(index, item);
65 }
66
67 static void children_removeLast(QQmlListProperty<QObject> *prop) {
68 auto data = static_cast<QQmlObjectModelPrivate *>(prop->data);
69 data->remove(data->children.size() - 1, 1);
70 }
71
72 void markNewChild(QQmlObjectModel *q, QObject *item)
73 {
74 if (QJSEngine *engine = qjsEngine(q)) {
75 QV4::WriteBarrier::markCustom(engine->handle(), [&](QV4::MarkStack *markStack) {
76 QV4::QObjectWrapper::markWrapper(item, markStack);
77 });
78 }
79 }
80
81 void insert(int index, QObject *item) {
82 Q_Q(QQmlObjectModel);
83 children.insert(index, Item(item));
84 markNewChild(q, item);
85 for (int i = index, end = children.size(); i < end; ++i)
86 setIndex(i);
87 QQmlChangeSet changeSet;
88 changeSet.insert(index, 1);
89 emit q->modelUpdated(changeSet, false);
90 emit q->countChanged();
91 emit q->childrenChanged();
92 }
93
94 void replace(int index, QObject *item) {
95 Q_Q(QQmlObjectModel);
96 clearIndex(index);
97 children.replace(index, Item(item));
98 markNewChild(q, item);
99 setIndex(index);
100 QQmlChangeSet changeSet;
101 changeSet.change(index, 1);
102 emit q->modelUpdated(changeSet, false);
103 emit q->childrenChanged();
104 }
105
106 void move(int from, int to, int n) {
107 Q_Q(QQmlObjectModel);
108 if (from > to) {
109 // Only move forwards - flip if backwards moving
110 int tfrom = from;
111 int tto = to;
112 from = tto;
113 to = tto+n;
114 n = tfrom-tto;
115 }
116
117 QVarLengthArray<QQmlObjectModelPrivate::Item, 4> store;
118 for (int i = 0; i < to - from; ++i)
119 store.append(std::move(children[from + n + i]));
120 for (int i = 0; i < n; ++i)
121 store.append(std::move(children[from + i]));
122
123 for (int i = 0, end = store.count(); i < end; ++i) {
124 children[from + i] = std::move(store[i]);
125 setIndex(from + i);
126 }
127
128 QQmlChangeSet changeSet;
129 changeSet.move(from, to, n, ++moveId);
130 emit q->modelUpdated(changeSet, false);
131 emit q->childrenChanged();
132 }
133
134 void remove(int index, int n) {
135 Q_Q(QQmlObjectModel);
136 for (int i = index; i < index + n; ++i)
137 clearIndex(i);
138 children.erase(children.begin() + index, children.begin() + index + n);
139 for (int i = index, end = children.size(); i < end; ++i)
140 setIndex(i);
141 QQmlChangeSet changeSet;
142 changeSet.remove(index, n);
143 emit q->modelUpdated(changeSet, false);
144 emit q->countChanged();
145 emit q->childrenChanged();
146 }
147
148 void clear() {
149 Q_Q(QQmlObjectModel);
150 const auto copy = children;
151 for (const Item &child : copy)
152 emit q->destroyingItem(child.item());
153 remove(0, children.size());
154 }
155
156 int indexOf(QObject *item) const {
157 for (int i = 0; i < children.size(); ++i)
158 if (children.at(i).item() == item)
159 return i;
160 return -1;
161 }
162
163 void markChildren(QV4::MarkStack *markStack) const
164 {
165 for (const Item &child : children)
166 QV4::QObjectWrapper::markWrapper(child.item(), markStack);
167 }
168
169 quint64 _q_createJSWrapper(QQmlV4ExecutionEnginePtr engine);
170
171private:
172 void setIndex(int child, int index)
173 {
174 if (auto *attached = static_cast<QQmlObjectModelAttached *>(
175 qmlAttachedPropertiesObject<QQmlObjectModel>(children.at(child).item()))) {
176 attached->setIndex(index);
177 }
178 }
179
180 void setIndex(int child) { setIndex(child, child); }
181 void clearIndex(int child) { setIndex(child, -1); }
182
183
184 uint moveId;
185 QList<Item> children;
186};
187
188namespace QV4 {
189namespace Heap {
191 static void markObjects(Base *that, MarkStack *markStack)
192 {
193 Q_ASSERT(QV4::Value::fromHeapObject(that).as<QV4::QObjectWrapper>());
194 QObject *object = static_cast<QObjectWrapper *>(that)->object();
195 if (!object)
196 return;
197
198 Q_ASSERT(qobject_cast<QQmlObjectModel *>(object));
199 QQmlObjectModelPrivate::get(static_cast<QQmlObjectModel *>(object))
200 ->markChildren(markStack);
201
202 QObjectWrapper::markObjects(that, markStack);
203 }
204};
205} // namespace Heap
206
208 V4_OBJECT2(QQmlObjectModelWrapper, QObjectWrapper)
209};
210
211} // namspace QV4
212
214
215quint64 QQmlObjectModelPrivate::_q_createJSWrapper(QQmlV4ExecutionEnginePtr engine)
216{
217 return (engine->memoryManager->allocate<QV4::QQmlObjectModelWrapper>(
218 q_func()))->asReturnedValue();
219}
220
221
222
223/*!
224 \qmltype ObjectModel
225 \nativetype QQmlObjectModel
226 \inqmlmodule QtQml.Models
227 \ingroup qtquick-models
228 \brief Defines a set of items to be used as a model.
229
230 An ObjectModel contains the visual items to be used in a view.
231 When an ObjectModel is used in a view, the view does not require
232 a delegate since the ObjectModel already contains the visual
233 delegate (items).
234
235 An item can determine its index within the
236 model via the \l{ObjectModel::index}{index} attached property.
237
238 The example below places three colored rectangles in a ListView.
239 \code
240 import QtQuick 2.0
241 import QtQml.Models 2.1
242
243 Rectangle {
244 ObjectModel {
245 id: itemModel
246 Rectangle { height: 30; width: 80; color: "red" }
247 Rectangle { height: 30; width: 80; color: "green" }
248 Rectangle { height: 30; width: 80; color: "blue" }
249 }
250
251 ListView {
252 anchors.fill: parent
253 model: itemModel
254 }
255 }
256 \endcode
257
258 \image objectmodel.png
259
260 \sa {Qt Quick Examples - Views}
261*/
262
263QQmlObjectModel::QQmlObjectModel(QObject *parent)
264 : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent)
265{
266}
267
268QQmlObjectModel::~QQmlObjectModel() = default;
269
270/*!
271 \qmlattachedproperty int QtQml.Models::ObjectModel::index
272 This attached property holds the index of this delegate's item within the model.
273
274 It is attached to each instance of the delegate.
275*/
276
277QQmlListProperty<QObject> QQmlObjectModel::children()
278{
279 Q_D(QQmlObjectModel);
280 return QQmlListProperty<QObject>(this, d,
281 QQmlObjectModelPrivate::children_append,
282 QQmlObjectModelPrivate::children_count,
283 QQmlObjectModelPrivate::children_at,
284 QQmlObjectModelPrivate::children_clear,
285 QQmlObjectModelPrivate::children_replace,
286 QQmlObjectModelPrivate::children_removeLast);
287}
288
289/*!
290 \qmlproperty int QtQml.Models::ObjectModel::count
291
292 The number of items in the model. This property is readonly.
293*/
294int QQmlObjectModel::count() const
295{
296 Q_D(const QQmlObjectModel);
297 return d->children.size();
298}
299
300bool QQmlObjectModel::isValid() const
301{
302 return true;
303}
304
305QObject *QQmlObjectModel::object(int index, QQmlIncubator::IncubationMode)
306{
307 Q_D(QQmlObjectModel);
308 QQmlObjectModelPrivate::Item &item = d->children[index];
309 item.addRef();
310 QObject *obj = item.item();
311 if (item.refCount() == 1) {
312 emit initItem(index, obj);
313 emit createdItem(index, obj);
314 }
315 return obj;
316}
317
318QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item, ReusableFlag)
319{
320 Q_D(QQmlObjectModel);
321 int idx = d->indexOf(item);
322 if (idx >= 0) {
323 if (!d->children[idx].deref())
324 return QQmlInstanceModel::Referenced;
325 }
326 return {};
327}
328
329QVariant QQmlObjectModel::variantValue(int index, const QString &role)
330{
331 Q_D(QQmlObjectModel);
332 if (index < 0 || index >= d->children.size())
333 return QString();
334 if (QObject *item = d->children.at(index).item())
335 return item->property(role.toUtf8().constData());
336 return QString();
337}
338
339QQmlIncubator::Status QQmlObjectModel::incubationStatus(int)
340{
341 return QQmlIncubator::Ready;
342}
343
344int QQmlObjectModel::indexOf(QObject *item, QObject *) const
345{
346 Q_D(const QQmlObjectModel);
347 return d->indexOf(item);
348}
349
350QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj)
351{
352 return new QQmlObjectModelAttached(obj);
353}
354
355/*!
356 \qmlmethod object QtQml.Models::ObjectModel::get(int index)
357 \since 5.6
358
359 Returns the item at \a index in the model. This allows the item
360 to be accessed or modified from JavaScript:
361
362 \code
363 Component.onCompleted: {
364 objectModel.append(objectComponent.createObject())
365 console.log(objectModel.get(0).objectName);
366 objectModel.get(0).objectName = "first";
367 }
368 \endcode
369
370 The \a index must be an element in the list.
371
372 \sa append()
373*/
374QObject *QQmlObjectModel::get(int index) const
375{
376 Q_D(const QQmlObjectModel);
377 if (index < 0 || index >= d->children.size())
378 return nullptr;
379 return d->children.at(index).item();
380}
381
382/*!
383 \qmlmethod QtQml.Models::ObjectModel::append(object item)
384 \since 5.6
385
386 Appends a new \a item to the end of the model.
387
388 \code
389 objectModel.append(objectComponent.createObject())
390 \endcode
391
392 \sa insert(), remove()
393*/
394void QQmlObjectModel::append(QObject *object)
395{
396 Q_D(QQmlObjectModel);
397 d->insert(count(), object);
398}
399
400/*!
401 \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item)
402 \since 5.6
403
404 Inserts a new \a item to the model at position \a index.
405
406 \code
407 objectModel.insert(2, objectComponent.createObject())
408 \endcode
409
410 The \a index must be to an existing item in the list, or one past
411 the end of the list (equivalent to append).
412
413 \sa append(), remove()
414*/
415void QQmlObjectModel::insert(int index, QObject *object)
416{
417 Q_D(QQmlObjectModel);
418 if (index < 0 || index > count()) {
419 qmlWarning(this) << tr("insert: index %1 out of range").arg(index);
420 return;
421 }
422 d->insert(index, object);
423}
424
425/*!
426 \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1)
427 \since 5.6
428
429 Moves \a n items \a from one position \a to another.
430
431 The from and to ranges must exist; for example, to move the first 3 items
432 to the end of the model:
433
434 \code
435 objectModel.move(0, objectModel.count - 3, 3)
436 \endcode
437
438 \sa append()
439*/
440void QQmlObjectModel::move(int from, int to, int n)
441{
442 Q_D(QQmlObjectModel);
443 if (n <= 0 || from == to)
444 return;
445 if (from < 0 || to < 0 || from + n > count() || to + n > count()) {
446 qmlWarning(this) << tr("move: out of range");
447 return;
448 }
449 d->move(from, to, n);
450}
451
452/*!
453 \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1)
454 \since 5.6
455
456 Removes \a n items at \a index from the model.
457
458 \sa clear()
459*/
460void QQmlObjectModel::remove(int index, int n)
461{
462 Q_D(QQmlObjectModel);
463 if (index < 0 || n <= 0 || index + n > count()) {
464 qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count());
465 return;
466 }
467 d->remove(index, n);
468}
469
470/*!
471 \qmlmethod QtQml.Models::ObjectModel::clear()
472 \since 5.6
473
474 Clears all items from the model.
475
476 \sa append(), remove()
477*/
478void QQmlObjectModel::clear()
479{
480 Q_D(QQmlObjectModel);
481 d->clear();
482}
483
484bool QQmlInstanceModel::setRequiredProperty(int index, const QString &name, const QVariant &value)
485{
486 Q_UNUSED(index);
487 Q_UNUSED(name);
488 Q_UNUSED(value);
489 // The view should not call this function, unless
490 // it's actually handled in a subclass.
491 Q_UNREACHABLE_RETURN(false);
492}
493
494QT_END_NAMESPACE
495
496#include "moc_qqmlobjectmodel_p.cpp"
static void children_replace(QQmlListProperty< QObject > *prop, qsizetype index, QObject *item)
static QObject * children_at(QQmlListProperty< QObject > *prop, qsizetype index)
int indexOf(QObject *item) const
void move(int from, int to, int n)
void replace(int index, QObject *item)
static void children_append(QQmlListProperty< QObject > *prop, QObject *item)
static void children_clear(QQmlListProperty< QObject > *prop)
static qsizetype children_count(QQmlListProperty< QObject > *prop)
static void children_removeLast(QQmlListProperty< QObject > *prop)
void insert(int index, QObject *item)
static QQmlObjectModelPrivate * get(QQmlObjectModel *q)
void remove(int index, int n)
void markChildren(QV4::MarkStack *markStack) const
void markNewChild(QQmlObjectModel *q, QObject *item)
quint64 _q_createJSWrapper(QQmlV4ExecutionEnginePtr engine)
Definition qjsvalue.h:23
DEFINE_OBJECT_VTABLE(QV4::QQmlObjectModelWrapper)
static void markObjects(Base *that, MarkStack *markStack)