1 | // Copyright (C) 2018 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 "qqmltableinstancemodel_p.h" |
5 | #include "qqmlabstractdelegatecomponent_p.h" |
6 | |
7 | #include <QtCore/QTimer> |
8 | |
9 | #include <QtQml/private/qqmlincubator_p.h> |
10 | #include <QtQmlModels/private/qqmlchangeset_p.h> |
11 | #include <QtQml/private/qqmlcomponent_p.h> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | const char* kModelItemTag = "_tableinstancemodel_modelItem"; |
16 | |
17 | bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem) |
18 | { |
19 | if (!modelItem->incubationTask) |
20 | return true; |
21 | |
22 | const auto status = modelItem->incubationTask->status(); |
23 | return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error); |
24 | } |
25 | |
26 | void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem) |
27 | { |
28 | Q_ASSERT(modelItem); |
29 | |
30 | delete modelItem->object; |
31 | modelItem->object = nullptr; |
32 | modelItem->contextData.reset(); |
33 | modelItem->deleteLater(); |
34 | } |
35 | |
36 | QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent) |
37 | : QQmlInstanceModel(*(new QObjectPrivate()), parent) |
38 | , m_qmlContext(qmlContext) |
39 | , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList()), |
40 | QQmlRefPointer<QQmlDelegateModelItemMetaType>::Adopt) |
41 | { |
42 | } |
43 | |
44 | void QQmlTableInstanceModel::useImportVersion(QTypeRevision version) |
45 | { |
46 | m_adaptorModel.useImportVersion(revision: version); |
47 | } |
48 | |
49 | QQmlTableInstanceModel::~QQmlTableInstanceModel() |
50 | { |
51 | for (const auto modelItem : m_modelItems) { |
52 | // No item in m_modelItems should be referenced at this point. The view |
53 | // should release all its items before it deletes this model. Only model items |
54 | // that are still being incubated should be left for us to delete. |
55 | Q_ASSERT(modelItem->objectRef == 0); |
56 | Q_ASSERT(modelItem->incubationTask); |
57 | // Check that we are not being deleted while we're |
58 | // in the process of e.g emitting a created signal. |
59 | Q_ASSERT(modelItem->scriptRef == 0); |
60 | |
61 | if (modelItem->object) { |
62 | delete modelItem->object; |
63 | modelItem->object = nullptr; |
64 | modelItem->contextData.reset(); |
65 | } |
66 | } |
67 | |
68 | deleteAllFinishedIncubationTasks(); |
69 | qDeleteAll(c: m_modelItems); |
70 | drainReusableItemsPool(maxPoolTime: 0); |
71 | } |
72 | |
73 | QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index) |
74 | { |
75 | if (m_delegateChooser) { |
76 | const int row = m_adaptorModel.rowAt(index); |
77 | const int column = m_adaptorModel.columnAt(index); |
78 | QQmlComponent *delegate = nullptr; |
79 | QQmlAbstractDelegateComponent *chooser = m_delegateChooser; |
80 | do { |
81 | delegate = chooser->delegate(adaptorModel: &m_adaptorModel, row, column); |
82 | chooser = qobject_cast<QQmlAbstractDelegateComponent *>(object: delegate); |
83 | } while (chooser); |
84 | return delegate; |
85 | } |
86 | |
87 | return m_delegate; |
88 | } |
89 | |
90 | QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index) |
91 | { |
92 | // Check if an item for the given index is already loaded and ready |
93 | QQmlDelegateModelItem *modelItem = m_modelItems.value(key: index, defaultValue: nullptr); |
94 | if (modelItem) |
95 | return modelItem; |
96 | |
97 | QQmlComponent *delegate = resolveDelegate(index); |
98 | if (!delegate) |
99 | return nullptr; |
100 | |
101 | // Check if the pool contains an item that can be reused |
102 | modelItem = m_reusableItemsPool.takeItem(delegate, newIndexHint: index); |
103 | if (modelItem) { |
104 | reuseItem(item: modelItem, newModelIndex: index); |
105 | m_modelItems.insert(key: index, value: modelItem); |
106 | return modelItem; |
107 | } |
108 | |
109 | // Create a new item from scratch |
110 | modelItem = m_adaptorModel.createItem(metaType: m_metaType.data(), index); |
111 | if (modelItem) { |
112 | modelItem->delegate = delegate; |
113 | m_modelItems.insert(key: index, value: modelItem); |
114 | return modelItem; |
115 | } |
116 | |
117 | qWarning() << Q_FUNC_INFO << "failed creating a model item for index: "<< index; |
118 | return nullptr; |
119 | } |
120 | |
121 | QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode) |
122 | { |
123 | Q_ASSERT(m_delegate); |
124 | Q_ASSERT(index >= 0 && index < m_adaptorModel.count()); |
125 | |
126 | QQmlDelegateModelItem *modelItem = resolveModelItem(index); |
127 | if (!modelItem) |
128 | return nullptr; |
129 | |
130 | if (modelItem->object) { |
131 | // The model item has already been incubated. So |
132 | // just bump the ref-count and return it. |
133 | modelItem->referenceObject(); |
134 | return modelItem->object; |
135 | } |
136 | |
137 | // The object is not ready, and needs to be incubated |
138 | incubateModelItem(modelItem, incubationMode); |
139 | if (!isDoneIncubating(modelItem)) |
140 | return nullptr; |
141 | |
142 | // Incubation is done, so the task should be removed |
143 | Q_ASSERT(!modelItem->incubationTask); |
144 | |
145 | if (!modelItem->object) { |
146 | // The object was incubated synchronously (otherwise we would return above). But since |
147 | // we have no object, the incubation must have failed. And when we have no object, there |
148 | // should be no object references either. And there should also not be any internal script |
149 | // refs at this point. So we delete the model item. |
150 | Q_ASSERT(!modelItem->isObjectReferenced()); |
151 | Q_ASSERT(!modelItem->isReferenced()); |
152 | m_modelItems.remove(key: modelItem->index); |
153 | delete modelItem; |
154 | return nullptr; |
155 | } |
156 | |
157 | // Incubation was completed sync and successful |
158 | modelItem->referenceObject(); |
159 | return modelItem->object; |
160 | } |
161 | |
162 | QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable) |
163 | { |
164 | Q_ASSERT(object); |
165 | auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(v: object->property(name: kModelItemTag)); |
166 | Q_ASSERT(modelItem); |
167 | // Ensure that the object was incubated by this QQmlTableInstanceModel |
168 | Q_ASSERT(m_modelItems.contains(modelItem->index)); |
169 | Q_ASSERT(m_modelItems[modelItem->index]->object == object); |
170 | |
171 | if (!modelItem->releaseObject()) |
172 | return QQmlDelegateModel::Referenced; |
173 | |
174 | if (modelItem->isReferenced()) { |
175 | // We still have an internal reference to this object, which means that we are told to release an |
176 | // object while the createdItem signal for it is still on the stack. This can happen when objects |
177 | // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch |
178 | // up with. The view might then find that the object is no longer visible and should be released. |
179 | // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers |
180 | // point of view, it should consider it destroyed. |
181 | return QQmlDelegateModel::Destroyed; |
182 | } |
183 | |
184 | // The item is not referenced by anyone |
185 | m_modelItems.remove(key: modelItem->index); |
186 | |
187 | if (reusable == Reusable) { |
188 | m_reusableItemsPool.insertItem(modelItem); |
189 | emit itemPooled(index: modelItem->index, object: modelItem->object); |
190 | return QQmlInstanceModel::Pooled; |
191 | } |
192 | |
193 | // The item is not reused or referenced by anyone, so just delete it |
194 | destroyModelItem(modelItem, mode: Deferred); |
195 | return QQmlInstanceModel::Destroyed; |
196 | } |
197 | |
198 | void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode) |
199 | { |
200 | emit destroyingItem(object: modelItem->object); |
201 | if (mode == Deferred) |
202 | modelItem->destroyObject(); |
203 | else |
204 | delete modelItem->object; |
205 | delete modelItem; |
206 | } |
207 | |
208 | void QQmlTableInstanceModel::dispose(QObject *object) |
209 | { |
210 | Q_ASSERT(object); |
211 | auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(v: object->property(name: kModelItemTag)); |
212 | Q_ASSERT(modelItem); |
213 | |
214 | modelItem->releaseObject(); |
215 | |
216 | // The item is not referenced by anyone |
217 | Q_ASSERT(!modelItem->isObjectReferenced()); |
218 | Q_ASSERT(!modelItem->isReferenced()); |
219 | // Ensure that the object was incubated by this QQmlTableInstanceModel |
220 | Q_ASSERT(m_modelItems.contains(modelItem->index)); |
221 | Q_ASSERT(m_modelItems[modelItem->index]->object == object); |
222 | |
223 | m_modelItems.remove(key: modelItem->index); |
224 | |
225 | emit destroyingItem(object); |
226 | delete object; |
227 | delete modelItem; |
228 | } |
229 | |
230 | void QQmlTableInstanceModel::cancel(int index) |
231 | { |
232 | auto modelItem = m_modelItems.value(key: index); |
233 | Q_ASSERT(modelItem); |
234 | |
235 | // Since the view expects the item to be incubating, there should be |
236 | // an incubation task. And since the incubation is not done, no-one |
237 | // should yet have received, and therfore hold a reference to, the object. |
238 | Q_ASSERT(modelItem->incubationTask); |
239 | Q_ASSERT(!modelItem->isObjectReferenced()); |
240 | |
241 | m_modelItems.remove(key: index); |
242 | |
243 | if (modelItem->object) |
244 | delete modelItem->object; |
245 | |
246 | // modelItem->incubationTask will be deleted from the modelItems destructor |
247 | delete modelItem; |
248 | } |
249 | |
250 | void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime) |
251 | { |
252 | m_reusableItemsPool.drain(maxPoolTime, releaseItem: [this](QQmlDelegateModelItem *modelItem) { |
253 | destroyModelItem(modelItem, mode: Immediate); |
254 | }); |
255 | } |
256 | |
257 | void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex) |
258 | { |
259 | // Update the context properties index, row and column on |
260 | // the delegate item, and inform the application about it. |
261 | // Note that we set alwaysEmit to true, to force all bindings |
262 | // to be reevaluated, even if the index didn't change (since |
263 | // the model can have changed size since last usage). |
264 | const bool alwaysEmit = true; |
265 | const int newRow = m_adaptorModel.rowAt(index: newModelIndex); |
266 | const int newColumn = m_adaptorModel.columnAt(index: newModelIndex); |
267 | item->setModelIndex(idx: newModelIndex, newRow, newColumn, alwaysEmit); |
268 | |
269 | // Notify the application that all 'dynamic'/role-based context data has |
270 | // changed as well (their getter function will use the updated index). |
271 | auto const itemAsList = QList<QQmlDelegateModelItem *>() << item; |
272 | auto const updateAllRoles = QVector<int>(); |
273 | m_adaptorModel.notify(items: itemAsList, index: newModelIndex, count: 1, roles: updateAllRoles); |
274 | |
275 | // Inform the view that the item is recycled. This will typically result |
276 | // in the view updating its own attached delegate item properties. |
277 | emit itemReused(index: newModelIndex, object: item->object); |
278 | } |
279 | |
280 | void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode) |
281 | { |
282 | // Guard the model item temporarily so that it's not deleted from |
283 | // incubatorStatusChanged(), in case the incubation is done synchronously. |
284 | modelItem->scriptRef++; |
285 | |
286 | if (modelItem->incubationTask) { |
287 | // We're already incubating the model item from a previous request. If the previous call requested |
288 | // the item async, but the current request needs it sync, we need to force-complete the incubation. |
289 | const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); |
290 | if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) |
291 | modelItem->incubationTask->forceCompletion(); |
292 | } else if (m_qmlContext && m_qmlContext->isValid()) { |
293 | modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); |
294 | |
295 | QQmlContext *creationContext = modelItem->delegate->creationContext(); |
296 | const QQmlRefPointer<QQmlContextData> componentContext |
297 | = QQmlContextData::get(context: creationContext ? creationContext : m_qmlContext.data()); |
298 | |
299 | QQmlComponentPrivate *cp = QQmlComponentPrivate::get(c: modelItem->delegate); |
300 | if (cp->isBound()) { |
301 | modelItem->contextData = componentContext; |
302 | cp->incubateObject( |
303 | incubationTask: modelItem->incubationTask, |
304 | component: modelItem->delegate, |
305 | engine: m_qmlContext->engine(), |
306 | context: componentContext, |
307 | forContext: QQmlContextData::get(context: m_qmlContext)); |
308 | } else { |
309 | QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted( |
310 | parent: QQmlContextData::get(context: creationContext ? creationContext : m_qmlContext.data())); |
311 | ctxt->setContextObject(modelItem); |
312 | modelItem->contextData = ctxt; |
313 | |
314 | cp->incubateObject( |
315 | incubationTask: modelItem->incubationTask, |
316 | component: modelItem->delegate, |
317 | engine: m_qmlContext->engine(), |
318 | context: ctxt, |
319 | forContext: QQmlContextData::get(context: m_qmlContext)); |
320 | } |
321 | } |
322 | |
323 | // Remove the temporary guard |
324 | modelItem->scriptRef--; |
325 | } |
326 | |
327 | void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status) |
328 | { |
329 | QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate; |
330 | Q_ASSERT(modelItem->incubationTask); |
331 | |
332 | modelItem->incubationTask = nullptr; |
333 | incubationTask->modelItemToIncubate = nullptr; |
334 | |
335 | if (status == QQmlIncubator::Ready) { |
336 | // Tag the incubated object with the model item for easy retrieval upon release etc. |
337 | modelItem->object->setProperty(name: kModelItemTag, value: QVariant::fromValue(value: modelItem)); |
338 | |
339 | // Emit that the item has been created. What normally happens next is that the view |
340 | // upon receiving the signal asks for the model item once more. And since the item is |
341 | // now in the map, it will be returned directly. |
342 | Q_ASSERT(modelItem->object); |
343 | modelItem->scriptRef++; |
344 | emit createdItem(index: modelItem->index, object: modelItem->object); |
345 | modelItem->scriptRef--; |
346 | } else if (status == QQmlIncubator::Error) { |
347 | qWarning() << "Error incubating delegate:"<< incubationTask->errors(); |
348 | } |
349 | |
350 | if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) { |
351 | // We have no internal reference to the model item, and the view has no |
352 | // reference to the incubated object. So just delete the model item. |
353 | // Note that being here means that the object was incubated _async_ |
354 | // (otherwise modelItem->isReferenced() would be true). |
355 | m_modelItems.remove(key: modelItem->index); |
356 | |
357 | if (modelItem->object) { |
358 | modelItem->scriptRef++; |
359 | emit destroyingItem(object: modelItem->object); |
360 | modelItem->scriptRef--; |
361 | Q_ASSERT(!modelItem->isReferenced()); |
362 | } |
363 | |
364 | deleteModelItemLater(modelItem); |
365 | } |
366 | |
367 | deleteIncubationTaskLater(incubationTask); |
368 | } |
369 | |
370 | QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) { |
371 | const auto modelItem = m_modelItems.value(key: index, defaultValue: nullptr); |
372 | if (!modelItem) |
373 | return QQmlIncubator::Null; |
374 | |
375 | if (modelItem->incubationTask) |
376 | return modelItem->incubationTask->status(); |
377 | |
378 | // Since we clear the incubation task when we're done |
379 | // incubating, it means that the status is Ready. |
380 | return QQmlIncubator::Ready; |
381 | } |
382 | |
383 | bool QQmlTableInstanceModel::setRequiredProperty(int index, const QString &name, const QVariant &value) |
384 | { |
385 | // This function can be called from the view upon |
386 | // receiving the initItem signal. It can be used to |
387 | // give all required delegate properties used by the |
388 | // view an initial value. |
389 | const auto modelItem = m_modelItems.value(key: index, defaultValue: nullptr); |
390 | if (!modelItem) |
391 | return false; |
392 | if (!modelItem->object) |
393 | return false; |
394 | if (!modelItem->incubationTask) |
395 | return false; |
396 | |
397 | bool wasInRequired = false; |
398 | const auto task = QQmlIncubatorPrivate::get(incubator: modelItem->incubationTask); |
399 | RequiredProperties *props = task->requiredProperties(); |
400 | if (props->empty()) |
401 | return false; |
402 | |
403 | QQmlProperty componentProp = QQmlComponentPrivate::removePropertyFromRequired( |
404 | createdComponent: modelItem->object, name, requiredProperties: props, engine: QQmlEnginePrivate::get(p: task->enginePriv), |
405 | wasInRequiredProperties: &wasInRequired); |
406 | if (wasInRequired) |
407 | componentProp.write(value); |
408 | return wasInRequired; |
409 | } |
410 | |
411 | void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask) |
412 | { |
413 | // We often need to post-delete incubation tasks, since we cannot |
414 | // delete them while we're in the middle of an incubation change callback. |
415 | Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask)); |
416 | m_finishedIncubationTasks.append(t: incubationTask); |
417 | if (m_finishedIncubationTasks.size() == 1) |
418 | QTimer::singleShot(interval: 1, receiver: this, slot: &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks); |
419 | } |
420 | |
421 | void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks() |
422 | { |
423 | qDeleteAll(c: m_finishedIncubationTasks); |
424 | m_finishedIncubationTasks.clear(); |
425 | } |
426 | |
427 | QVariant QQmlTableInstanceModel::model() const |
428 | { |
429 | return m_adaptorModel.model(); |
430 | } |
431 | |
432 | void QQmlTableInstanceModel::setModel(const QVariant &model) |
433 | { |
434 | // Pooled items are still accessible/alive for the application, and |
435 | // needs to stay in sync with the model. So we need to drain the pool |
436 | // completely when the model changes. |
437 | drainReusableItemsPool(maxPoolTime: 0); |
438 | if (auto const aim = abstractItemModel()) { |
439 | disconnect(sender: aim, signal: &QAbstractItemModel::dataChanged, receiver: this, slot: &QQmlTableInstanceModel::dataChangedCallback); |
440 | disconnect(sender: aim, signal: &QAbstractItemModel::modelAboutToBeReset, receiver: this, slot: &QQmlTableInstanceModel::modelAboutToBeResetCallback); |
441 | } |
442 | m_adaptorModel.setModel(model); |
443 | if (auto const aim = abstractItemModel()) { |
444 | connect(sender: aim, signal: &QAbstractItemModel::dataChanged, context: this, slot: &QQmlTableInstanceModel::dataChangedCallback); |
445 | connect(sender: aim, signal: &QAbstractItemModel::modelAboutToBeReset, context: this, slot: &QQmlTableInstanceModel::modelAboutToBeResetCallback); |
446 | } |
447 | } |
448 | |
449 | void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) |
450 | { |
451 | // This function is called when model data has changed. In that case, we tell the adaptor model |
452 | // to go through all the items we have created, find the ones that are affected, and notify that |
453 | // their model data has changed. This will in turn update QML bindings inside the delegate items. |
454 | int numberOfRowsChanged = end.row() - begin.row() + 1; |
455 | int numberOfColumnsChanged = end.column() - begin.column() + 1; |
456 | |
457 | for (int column = 0; column < numberOfColumnsChanged; ++column) { |
458 | const int columnIndex = begin.column() + column; |
459 | const int rowIndex = begin.row() + (columnIndex * rows()); |
460 | m_adaptorModel.notify(items: m_modelItems.values(), index: rowIndex, count: numberOfRowsChanged, roles); |
461 | } |
462 | } |
463 | |
464 | void QQmlTableInstanceModel::modelAboutToBeResetCallback() |
465 | { |
466 | // When the model is reset, we can no longer rely on any of the data it has |
467 | // provided us so far. Normally it's enough for the view to recreate all the |
468 | // delegate items in that case, except if the model roles has changed as well |
469 | // (since those are cached by QQmlAdaptorModel / Accessors). For the latter case, we |
470 | // simply set the model once more in the delegate model to rebuild everything. |
471 | auto const aim = abstractItemModel(); |
472 | auto oldRoleNames = aim->roleNames(); |
473 | QObject::connect(sender: aim, signal: &QAbstractItemModel::modelReset, context: this, slot: [this, aim, oldRoleNames](){ |
474 | if (oldRoleNames != aim->roleNames()) |
475 | setModel(model()); |
476 | }, type: Qt::SingleShotConnection); |
477 | } |
478 | |
479 | QQmlComponent *QQmlTableInstanceModel::delegate() const |
480 | { |
481 | return m_delegate; |
482 | } |
483 | |
484 | void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate) |
485 | { |
486 | if (m_delegate == delegate) |
487 | return; |
488 | |
489 | m_delegateChooser = nullptr; |
490 | if (delegate) { |
491 | QQmlAbstractDelegateComponent *adc = |
492 | qobject_cast<QQmlAbstractDelegateComponent *>(object: delegate); |
493 | if (adc) |
494 | m_delegateChooser = adc; |
495 | } |
496 | |
497 | m_delegate = delegate; |
498 | } |
499 | |
500 | const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const |
501 | { |
502 | return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() : nullptr; |
503 | } |
504 | |
505 | // -------------------------------------------------------- |
506 | |
507 | void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object) |
508 | { |
509 | initializeRequiredProperties(modelItemToIncubate, object); |
510 | modelItemToIncubate->object = object; |
511 | emit tableInstanceModel->initItem(index: modelItemToIncubate->index, object); |
512 | |
513 | if (!QQmlIncubatorPrivate::get(incubator: this)->requiredProperties()->empty()) { |
514 | modelItemToIncubate->object = nullptr; |
515 | object->deleteLater(); |
516 | } |
517 | } |
518 | |
519 | void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status) |
520 | { |
521 | if (!QQmlTableInstanceModel::isDoneIncubating(modelItem: modelItemToIncubate)) |
522 | return; |
523 | |
524 | // We require the view to cancel any ongoing load |
525 | // requests before the tableInstanceModel is destructed. |
526 | Q_ASSERT(tableInstanceModel); |
527 | |
528 | tableInstanceModel->incubatorStatusChanged(incubationTask: this, status); |
529 | } |
530 | |
531 | QT_END_NAMESPACE |
532 | |
533 | #include "moc_qqmltableinstancemodel_p.cpp" |
534 | |
535 |
Definitions
- kModelItemTag
- isDoneIncubating
- deleteModelItemLater
- QQmlTableInstanceModel
- useImportVersion
- ~QQmlTableInstanceModel
- resolveDelegate
- resolveModelItem
- object
- release
- destroyModelItem
- dispose
- cancel
- drainReusableItemsPool
- reuseItem
- incubateModelItem
- incubatorStatusChanged
- incubationStatus
- setRequiredProperty
- deleteIncubationTaskLater
- deleteAllFinishedIncubationTasks
- model
- setModel
- dataChangedCallback
- modelAboutToBeResetCallback
- delegate
- setDelegate
- abstractItemModel
- setInitialState
Start learning QML with our Intro Training
Find out more