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