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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/qmlmodels/qqmlobjectmodel.cpp