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 {Three colored rectangles stacked in a ListView}
260
261 \sa {Qt Quick Examples - Views}
262*/
263
264QQmlObjectModel::QQmlObjectModel(QObject *parent)
265 : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent)
266{
267}
268
269QQmlObjectModel::~QQmlObjectModel() = default;
270
271/*!
272 \qmlattachedproperty int QtQml.Models::ObjectModel::index
273 This attached property holds the index of this delegate's item within the model.
274
275 It is attached to each instance of the delegate.
276*/
277
278QQmlListProperty<QObject> QQmlObjectModel::children()
279{
280 Q_D(QQmlObjectModel);
281 return QQmlListProperty<QObject>(this, d,
282 QQmlObjectModelPrivate::children_append,
283 QQmlObjectModelPrivate::children_count,
284 QQmlObjectModelPrivate::children_at,
285 QQmlObjectModelPrivate::children_clear,
286 QQmlObjectModelPrivate::children_replace,
287 QQmlObjectModelPrivate::children_removeLast);
288}
289
290/*!
291 \qmlproperty int QtQml.Models::ObjectModel::count
292
293 The number of items in the model. This property is readonly.
294*/
295int QQmlObjectModel::count() const
296{
297 Q_D(const QQmlObjectModel);
298 return d->children.size();
299}
300
301bool QQmlObjectModel::isValid() const
302{
303 return true;
304}
305
306QObject *QQmlObjectModel::object(int index, QQmlIncubator::IncubationMode)
307{
308 Q_D(QQmlObjectModel);
309 QQmlObjectModelPrivate::Item &item = d->children[index];
310 item.addRef();
311 QObject *obj = item.item();
312 if (item.refCount() == 1) {
313 emit initItem(index, obj);
314 emit createdItem(index, obj);
315 }
316 return obj;
317}
318
319QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item, ReusableFlag)
320{
321 Q_D(QQmlObjectModel);
322 int idx = d->indexOf(item);
323 if (idx >= 0) {
324 if (!d->children[idx].deref())
325 return QQmlInstanceModel::Referenced;
326 }
327 return {};
328}
329
330QVariant QQmlObjectModel::variantValue(int index, const QString &role)
331{
332 Q_D(QQmlObjectModel);
333 if (index < 0 || index >= d->children.size())
334 return QString();
335 if (QObject *item = d->children.at(index).item())
336 return item->property(role.toUtf8().constData());
337 return QString();
338}
339
340QQmlIncubator::Status QQmlObjectModel::incubationStatus(int)
341{
342 return QQmlIncubator::Ready;
343}
344
345int QQmlObjectModel::indexOf(QObject *item, QObject *) const
346{
347 Q_D(const QQmlObjectModel);
348 return d->indexOf(item);
349}
350
351QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj)
352{
353 return new QQmlObjectModelAttached(obj);
354}
355
356/*!
357 \qmlmethod object QtQml.Models::ObjectModel::get(int index)
358 \since 5.6
359
360 Returns the item at \a index in the model. This allows the item
361 to be accessed or modified from JavaScript:
362
363 \code
364 Component.onCompleted: {
365 objectModel.append(objectComponent.createObject())
366 console.log(objectModel.get(0).objectName);
367 objectModel.get(0).objectName = "first";
368 }
369 \endcode
370
371 The \a index must be an element in the list.
372
373 \sa append()
374*/
375QObject *QQmlObjectModel::get(int index) const
376{
377 Q_D(const QQmlObjectModel);
378 if (index < 0 || index >= d->children.size())
379 return nullptr;
380 return d->children.at(index).item();
381}
382
383/*!
384 \qmlmethod void QtQml.Models::ObjectModel::append(object item)
385 \since 5.6
386
387 Appends a new \a item to the end of the model.
388
389 \code
390 objectModel.append(objectComponent.createObject())
391 \endcode
392
393 \sa insert(), remove()
394*/
395void QQmlObjectModel::append(QObject *object)
396{
397 Q_D(QQmlObjectModel);
398 d->insert(count(), object);
399}
400
401/*!
402 \qmlmethod void QtQml.Models::ObjectModel::insert(int index, object item)
403 \since 5.6
404
405 Inserts a new \a item to the model at position \a index.
406
407 \code
408 objectModel.insert(2, objectComponent.createObject())
409 \endcode
410
411 The \a index must be to an existing item in the list, or one past
412 the end of the list (equivalent to append).
413
414 \sa append(), remove()
415*/
416void QQmlObjectModel::insert(int index, QObject *object)
417{
418 Q_D(QQmlObjectModel);
419 if (index < 0 || index > count()) {
420 qmlWarning(this) << tr("insert: index %1 out of range").arg(index);
421 return;
422 }
423 d->insert(index, object);
424}
425
426/*!
427 \qmlmethod void QtQml.Models::ObjectModel::move(int from, int to, int n = 1)
428 \since 5.6
429
430 Moves \a n items \a from one position \a to another.
431
432 The from and to ranges must exist; for example, to move the first 3 items
433 to the end of the model:
434
435 \code
436 objectModel.move(0, objectModel.count - 3, 3)
437 \endcode
438
439 \sa append()
440*/
441void QQmlObjectModel::move(int from, int to, int n)
442{
443 Q_D(QQmlObjectModel);
444 if (n <= 0 || from == to)
445 return;
446 if (from < 0 || to < 0 || from + n > count() || to + n > count()) {
447 qmlWarning(this) << tr("move: out of range");
448 return;
449 }
450 d->move(from, to, n);
451}
452
453/*!
454 \qmlmethod void QtQml.Models::ObjectModel::remove(int index, int n = 1)
455 \since 5.6
456
457 Removes \a n items at \a index from the model.
458
459 \sa clear()
460*/
461void QQmlObjectModel::remove(int index, int n)
462{
463 Q_D(QQmlObjectModel);
464 if (index < 0 || n <= 0 || index + n > count()) {
465 qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count());
466 return;
467 }
468 d->remove(index, n);
469}
470
471/*!
472 \qmlmethod void QtQml.Models::ObjectModel::clear()
473 \since 5.6
474
475 Clears all items from the model.
476
477 \sa append(), remove()
478*/
479void QQmlObjectModel::clear()
480{
481 Q_D(QQmlObjectModel);
482 d->clear();
483}
484
485bool QQmlInstanceModel::setRequiredProperty(int index, const QString &name, const QVariant &value)
486{
487 Q_UNUSED(index);
488 Q_UNUSED(name);
489 Q_UNUSED(value);
490 // The view should not call this function, unless
491 // it's actually handled in a subclass.
492 Q_UNREACHABLE_RETURN(false);
493}
494
495QT_END_NAMESPACE
496
497#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)
Combined button and popup list for selecting options.
Definition qjsvalue.h:24
DEFINE_OBJECT_VTABLE(QV4::QQmlObjectModelWrapper)
static void markObjects(Base *that, MarkStack *markStack)