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
qqmltableinstancemodel.cpp
Go to the documentation of this file.
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// Qt-Security score:significant
4
7
8#include <QtCore/QTimer>
9
10#include <QtQml/private/qqmlincubator_p.h>
11#include <QtQmlModels/private/qqmlchangeset_p.h>
12#include <QtQml/private/qqmlcomponent_p.h>
13
14#include <QtCore/qtyperevision.h>
15
17
18const char* kModelItemTag = "_tableinstancemodel_modelItem";
19
20bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem)
21{
22 QQDMIncubationTask *incubationTask = modelItem->incubationTask();
23 if (!incubationTask)
24 return true;
25
26 switch (incubationTask->status()) {
27 case QQmlIncubator::Ready:
28 case QQmlIncubator::Error:
29 return true;
30 default:
31 break;
32 }
33
34 return false;
35}
36
37void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem)
38{
39 Q_ASSERT(modelItem);
40
41 modelItem->destroyObject();
42 modelItem->deleteLater();
43}
44
45QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent)
46 : QQmlInstanceModel(*(new QObjectPrivate()), parent)
47 , m_qmlContext(qmlContext)
48 , m_metaType(QQml::makeRefPointer<QQmlDelegateModelItemMetaType>(
49 m_qmlContext->engine()->handle(), this))
50{
51}
52
53void QQmlTableInstanceModel::useImportVersion(QTypeRevision version)
54{
55 m_adaptorModel.useImportVersion(version);
56}
57
58QQmlTableInstanceModel::~QQmlTableInstanceModel()
59{
60 for (const auto modelItem : m_modelItems) {
61 // No item in m_modelItems should be referenced at this point. The view
62 // should release all its items before it deletes this model. Only model items
63 // that are still being incubated should be left for us to delete.
64 // We can't rely on that, though. So we only check the strong ref.
65 Q_ASSERT(modelItem->objectStrongRef() == 0);
66 Q_ASSERT(modelItem->incubationTask());
67 // Check that we are not being deleted while we're
68 // in the process of e.g emitting a created signal.
69 Q_ASSERT(modelItem->scriptRef() == 0);
70
71 modelItem->destroyObject();
72 }
73
74 deleteAllFinishedIncubationTasks();
75 qDeleteAll(m_modelItems);
76 drainReusableItemsPool(0);
77
78 // We don't expect there to be any released items at this point.
79 // The view must explicitly call commitReleasedItems() after each
80 // rebuild of the viewport, and long before the model is destructed.
81 Q_ASSERT(m_releasedItems.isEmpty());
82}
83
84QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index)
85{
86 if (m_delegateChooser) {
87 const int row = m_adaptorModel.rowAt(index);
88 const int column = m_adaptorModel.columnAt(index);
89 QQmlComponent *delegate = nullptr;
90 QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
91 do {
92 delegate = chooser->delegate(&m_adaptorModel, row, column);
93 chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
94 } while (chooser);
95 return delegate;
96 }
97
98 return m_delegate;
99}
100
101QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int flatIndex, const QModelIndex &modelIndex)
102{
103 Q_ASSERT(flatIndex >= 0 && flatIndex < m_adaptorModel.count());
104
105 // Check if an item for the given index is already loaded and ready
106 if (QQmlDelegateModelItem *modelItem = m_modelItems.value(flatIndex, nullptr))
107 return modelItem;
108
109 QQmlComponent *delegate = resolveDelegate(flatIndex);
110 if (!delegate)
111 return nullptr;
112
113 // Check if the temporary release cache contains the requested item
114 if (modelIndex.isValid()) {
115 const auto it = std::find_if(m_releasedItems.cbegin(), m_releasedItems.cend(),
116 [&modelIndex](const QQmlDelegateModelItem *item) {
117 return item->persistentModelIndex() == modelIndex;
118 });
119
120 if (it != m_releasedItems.cend()) {
121 QQmlDelegateModelItem *modelItem = *it;
122 // Note: a rebuild can change which delegate a DelegateChooser assigns to a cell, so the
123 // released item may no longer use the right delegate, even if it has the correct index.
124 if (modelItem->delegate() == delegate) {
125 m_releasedItems.erase(it);
126 restoreFromReleasedItemsCache(modelItem, flatIndex);
127 return modelItem;
128 }
129 }
130 }
131
132 // Check if the pool contains an item with the same delegate that can be reused
133 if (QQmlDelegateModelItem *modelItem = m_reusableItemsPool.takeItem(delegate, flatIndex)) {
134 m_modelItems.insert(flatIndex, modelItem);
135 reuseItem(modelItem, flatIndex);
136 return modelItem;
137 }
138
139 // Create a new item from scratch
140 if (QQmlDelegateModelItem *modelItem = m_adaptorModel.createItem(m_metaType.data(), flatIndex)) {
141 modelItem->setDelegate(delegate);
142 m_modelItems.insert(flatIndex, modelItem);
143 return modelItem;
144 }
145
146 qWarning() << Q_FUNC_INFO << "failed creating a model item for index: " << flatIndex << modelIndex;
147 return nullptr;
148}
149
150void QQmlTableInstanceModel::commitReleasedItems()
151{
152 // Transfer all released items from the temporary cache to the reuse pool. From
153 // now on, they can only be reused based on delegate type and not model index.
154 for (auto *item : std::as_const(m_releasedItems)) {
155 item->setPersistentModelIndex(QModelIndex());
156 if (m_reusableItemsPool.insertItem(item))
157 emit itemPooled(item->modelIndex(), item->object());
158 else
159 destroyModelItem(item, Deferred);
160 }
161 m_releasedItems.clear();
162}
163
164QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode)
165{
166 Q_ASSERT(m_delegate);
167
168 QModelIndex modelIndex;
169 if (const QAbstractItemModel *aim = abstractItemModel()) {
170 // A valid QModelIndex is needed for resolveModelItem() to match
171 // items in the release cache rather than just delegate type alone.
172 const int row = m_adaptorModel.rowAt(index);
173 const int column = m_adaptorModel.columnAt(index);
174 modelIndex = aim->index(row, column);
175 }
176
177 if (QQmlDelegateModelItem *modelItem = resolveModelItem(index, modelIndex)) {
178 // Return the incubated object, or start an async incubation task and return nullptr for now
179 return incubateModelItemIfNeeded(modelItem, incubationMode);
180 }
181
182 return nullptr;
183}
184
185QObject *QQmlTableInstanceModel::incubateModelItemIfNeeded(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
186{
187 if (modelItem->object()) {
188 // The model item has already been incubated. So
189 // just bump the ref-count and return it.
190 return modelItem->referenceObjectWeak();
191 }
192
193 // The object is not ready, and needs to be incubated
194 incubateModelItem(modelItem, incubationMode);
195 if (!isDoneIncubating(modelItem))
196 return nullptr;
197
198 // Incubation is done, so the task should be removed
199 Q_ASSERT(!modelItem->incubationTask());
200
201 // Incubation was completed sync and successful
202 if (modelItem->object())
203 return modelItem->referenceObjectWeak();
204
205 // The object was incubated synchronously (otherwise we would return above). But since
206 // we have no object, the incubation must have failed. And when we have no object, there
207 // should be no object references either. And there should also not be any internal script
208 // refs at this point. So we delete the model item.
209 Q_ASSERT(!modelItem->isObjectReferenced());
210 Q_ASSERT(!modelItem->isScriptReferenced());
211 m_modelItems.remove(modelItem->modelIndex());
212 delete modelItem;
213 return nullptr;
214}
215
216QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable)
217{
218 Q_ASSERT(object);
219 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
220 Q_ASSERT(modelItem);
221 // Ensure that the object was incubated by this QQmlTableInstanceModel
222 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
223 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
224
225 if (!modelItem->releaseObjectWeak())
226 return QQmlDelegateModel::Referenced;
227
228 if (modelItem->isScriptReferenced()) {
229 // We still have an internal reference to this object, which means that we are told to release an
230 // object while the createdItem signal for it is still on the stack. This can happen when objects
231 // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch
232 // up with. The view might then find that the object is no longer visible and should be released.
233 // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers
234 // point of view, it should consider it destroyed.
235 return QQmlDelegateModel::Destroyed;
236 }
237
238 // The item is not referenced by anyone
239 m_modelItems.remove(modelItem->modelIndex());
240
241 if (reusable == Reusable) {
242 // Stage the item in a temporary cache rather than moving it directly to the reuse pool.
243 // The view calls commitReleasedItems() once the rebuild is done, which transfers the cache
244 // to the pool. Keeping a temporary cache lets resolveModelItem() match a staged item by
245 // persistent model index first, during viewport builds, and hand back the exact same
246 // delegate item that was showing that cell. That way we can avoid the otherwise full
247 // reuse of an item, which includes emitting pooled signals etc. This also preserves
248 // ongoing visual state such as animations.
249 m_releasedItems.append(modelItem);
250 return QQmlInstanceModel::Pooled;
251 }
252
253 // The item is not reused or referenced by anyone, so just delete it
254 destroyModelItem(modelItem, Deferred);
255 return QQmlInstanceModel::Destroyed;
256}
257
258void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode)
259{
260 emit destroyingItem(modelItem->object());
261 if (mode == Deferred)
262 modelItem->destroyObjectLater();
263 else
264 modelItem->destroyObject();
265 delete modelItem;
266}
267
268void QQmlTableInstanceModel::dispose(QObject *object)
269{
270 Q_ASSERT(object);
271 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
272 Q_ASSERT(modelItem);
273
274 modelItem->releaseObjectWeak();
275
276 // The item is not referenced by anyone
277 Q_ASSERT(!modelItem->isObjectReferenced());
278 Q_ASSERT(!modelItem->isScriptReferenced());
279 // Ensure that the object was incubated by this QQmlTableInstanceModel
280 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
281 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
282
283 m_modelItems.remove(modelItem->modelIndex());
284
285 emit destroyingItem(object);
286 modelItem->destroyObject();
287
288 delete modelItem;
289}
290
291void QQmlTableInstanceModel::cancel(int index)
292{
293 auto modelItem = m_modelItems.value(index);
294 Q_ASSERT(modelItem);
295
296 // Since the view expects the item to be incubating, there should be
297 // an incubation task. And since the incubation is not done, no-one
298 // should yet have received, and therfore hold a reference to, the object.
299 Q_ASSERT(modelItem->incubationTask());
300 Q_ASSERT(!modelItem->isObjectReferenced());
301
302 m_modelItems.remove(index);
303
304 modelItem->destroyObject();
305
306 // modelItem->incubationTask will be deleted from the modelItems destructor
307 delete modelItem;
308}
309
310void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime)
311{
312 m_reusableItemsPool.drain(maxPoolTime, [this](QQmlDelegateModelItem *modelItem) {
313 destroyModelItem(modelItem, Immediate);
314 });
315}
316
317void QQmlTableInstanceModel::restoreFromReleasedItemsCache(QQmlDelegateModelItem *item, int newFlatIndex)
318{
319 // Row/column shifts elsewhere in the table may have changed this item's
320 // position, so update its index before handing it back from the released
321 // items cache. Also notify the view so it can refresh any index-dependent
322 // required properties (such as 'expanded' and 'hasChildren').
323 m_modelItems.insert(newFlatIndex, item);
324 const bool alwaysEmit = true;
325 const bool init = false;
326 const int row = m_adaptorModel.rowAt(newFlatIndex);
327 const int column = m_adaptorModel.columnAt(newFlatIndex);
328 item->setModelIndex(newFlatIndex, row, column, alwaysEmit);
329 emit updateItemProperties(newFlatIndex, item->object(), init);
330}
331
332void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex)
333{
334 // Update the context properties index, row and column on
335 // the delegate item, and inform the application about it.
336 // Note that we set alwaysEmit to true, to force all bindings
337 // to be reevaluated, even if the index didn't change (since
338 // the model can have changed size since last usage).
339 const bool alwaysEmit = true;
340 const int newRow = m_adaptorModel.rowAt(newModelIndex);
341 const int newColumn = m_adaptorModel.columnAt(newModelIndex);
342 item->setModelIndex(newModelIndex, newRow, newColumn, alwaysEmit);
343 if (auto *qaim = abstractItemModel())
344 item->setPersistentModelIndex(qaim->index(newRow, newColumn));
345
346 // Notify the application that all 'dynamic'/role-based context data has
347 // changed as well (their getter function will use the updated index).
348 auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
349 auto const updateAllRoles = QList<int>();
350 m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
351
352 // Inform the view that the item is recycled. This will typically result
353 // in the view updating its own attached delegate item properties.
354 const bool init = false;
355 emit updateItemProperties(newModelIndex, item->object(), init);
356 emit itemReused(newModelIndex, item->object());
357}
358
359void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
360{
361 // Guard the model item temporarily so that it's not deleted from
362 // incubatorStatusChanged(), in case the incubation is done synchronously.
363 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
364
365 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask()) {
366 // We're already incubating the model item from a previous request. If the previous call requested
367 // the item async, but the current request needs it sync, we need to force-complete the incubation.
368 const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested);
369 if (sync && incubationTask->incubationMode() == QQmlIncubator::Asynchronous)
370 incubationTask->forceCompletion();
371 } else if (m_qmlContext && m_qmlContext->isValid()) {
372 modelItem->setIncubationTask(
373 new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode));
374 // TODO: In order to retain compatibility, we cannot allow the incubation task to clear the
375 // context object in the presence of required properties. This results in the context
376 // properties still being available in the delegate even though they shouldn't.
377 // modelItem->incubationTask->incubating = modelItem;
378
379 QQmlComponent *delegate = modelItem->delegate();
380 QQmlContext *creationContext = delegate->creationContext();
381 const QQmlRefPointer<QQmlContextData> componentContext
382 = QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data());
383
384 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate);
385 if (cp->isBound()) {
386 modelItem->setContextData(componentContext);
387
388 // Ignore return value of initProxy. We want to know the proxy when assigning required
389 // properties, but we don't want it to pollute our context. The context is bound.
390 if (m_adaptorModel.hasProxyObject())
391 modelItem->initProxy();
392
393 cp->incubateObject(
394 modelItem->incubationTask(), delegate, m_qmlContext->engine(), componentContext,
395 QQmlContextData::get(m_qmlContext));
396 } else {
397 QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted(
398 QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()));
399 ctxt->setContextObject(modelItem);
400 modelItem->setContextData(ctxt);
401
402 // If the model is read-only we cannot just expose the object as context
403 // We actually need a separate model object to moderate access.
404 if (m_adaptorModel.hasProxyObject()) {
405 if (m_adaptorModel.delegateModelAccess == QQmlDelegateModel::ReadOnly)
406 modelItem->initProxy();
407 else
408 ctxt = modelItem->initProxy();
409 }
410
411 cp->incubateObject(
412 modelItem->incubationTask(), modelItem->delegate(), m_qmlContext->engine(),
413 ctxt, QQmlContextData::get(m_qmlContext));
414 }
415 }
416}
417
418void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
419{
420 QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
421 Q_ASSERT(modelItem->incubationTask());
422
423 modelItem->clearIncubationTask();
424 incubationTask->modelItemToIncubate = nullptr;
425
426 if (status == QQmlIncubator::Ready) {
427 QObject *object = modelItem->object();
428 Q_ASSERT(object);
429
430 if (auto *qaim = abstractItemModel()) {
431 // Optimization: store a persistent model index so that resolveModelItem(QModelIndex)
432 // can match this item by index after a rebuild, even if its flat index has shifted
433 // due to row or column insertions/removals.
434 modelItem->setPersistentModelIndex(qaim->index(modelItem->modelRow(), modelItem->modelColumn()));
435 }
436
437 // Tag the incubated object with the model item for easy retrieval upon release etc.
438 object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
439
440 // Emit that the item has been created. What normally happens next is that the view
441 // upon receiving the signal asks for the model item once more. And since the item is
442 // now in the map, it will be returned directly.
443 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
444 emit createdItem(modelItem->modelIndex(), object);
445 } else if (status == QQmlIncubator::Error) {
446 qWarning() << "Error incubating delegate:" << incubationTask->errors();
447 }
448
449 if (!modelItem->isScriptReferenced() && !modelItem->isObjectReferenced()) {
450 // We have no internal reference to the model item, and the view has no
451 // reference to the incubated object. So just delete the model item.
452 // Note that being here means that the object was incubated _async_
453 // (otherwise modelItem->isReferenced() would be true).
454 m_modelItems.remove(modelItem->modelIndex());
455
456 if (QObject *object = modelItem->object()) {
457 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
458 emit destroyingItem(object);
459 }
460
461 Q_ASSERT(!modelItem->isScriptReferenced());
462 deleteModelItemLater(modelItem);
463 }
464
465 deleteIncubationTaskLater(incubationTask);
466}
467
468QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) {
469 const auto modelItem = m_modelItems.value(index, nullptr);
470 if (!modelItem)
471 return QQmlIncubator::Null;
472
473 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask())
474 return incubationTask->status();
475
476 // Since we clear the incubation task when we're done
477 // incubating, it means that the status is Ready.
478 return QQmlIncubator::Ready;
479}
480
481bool QQmlTableInstanceModel::setRequiredProperty(int index, const QString &name, const QVariant &value)
482{
483 // This function can be called from the view upon
484 // receiving the initItem signal. It can be used to
485 // give all required delegate properties used by the
486 // view an initial value.
487 const auto modelItem = m_modelItems.value(index, nullptr);
488 if (!modelItem)
489 return false;
490 if (!modelItem->object())
491 return false;
492 if (!modelItem->incubationTask())
493 return false;
494
495 bool wasInRequired = false;
496 const auto task = QQmlIncubatorPrivate::get(modelItem->incubationTask());
497 RequiredProperties *props = task->requiredProperties();
498 if (props->empty())
499 return false;
500
501 QQmlProperty componentProp = QQmlComponentPrivate::removePropertyFromRequired(
502 modelItem->object(), name, props, QQmlEnginePrivate::get(task->enginePriv),
503 &wasInRequired);
504 if (wasInRequired)
505 componentProp.write(value);
506 return wasInRequired;
507}
508
509QQmlDelegateModelItem *QQmlTableInstanceModel::getModelItem(int index)
510{
511 return m_modelItems.value(index, nullptr);
512}
513
514void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
515{
516 // We often need to post-delete incubation tasks, since we cannot
517 // delete them while we're in the middle of an incubation change callback.
518 Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask));
519 m_finishedIncubationTasks.append(incubationTask);
520 if (m_finishedIncubationTasks.size() == 1)
521 QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks);
522}
523
524void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
525{
526 qDeleteAll(m_finishedIncubationTasks);
527 m_finishedIncubationTasks.clear();
528}
529
530QVariant QQmlTableInstanceModel::model() const
531{
532 return m_adaptorModel.model();
533}
534
535void QQmlTableInstanceModel::forceSetModel(const QVariant &model)
536{
537 // Pooled items are still accessible/alive for the application, and
538 // needs to stay in sync with the model. So we need to drain the pool
539 // completely when the model changes.
540 drainReusableItemsPool(0);
541 if (auto const aim = abstractItemModel()) {
542 disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
543 disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
544 }
545 m_adaptorModel.setModel(model);
546 if (auto const aim = abstractItemModel()) {
547 connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
548 connect(aim, &QAbstractItemModel::modelAboutToBeReset, this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
549 }
550}
551
552void QQmlTableInstanceModel::setModel(const QVariant &model)
553{
554 if (m_adaptorModel.model() == model)
555 return;
556
557 forceSetModel(model);
558
559 emit modelChanged();
560}
561
562void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QList<int> &roles)
563{
564 // This function is called when model data has changed. In that case, we tell the adaptor model
565 // to go through all the items we have created, find the ones that are affected, and notify that
566 // their model data has changed. This will in turn update QML bindings inside the delegate items.
567 int numberOfRowsChanged = end.row() - begin.row() + 1;
568 int numberOfColumnsChanged = end.column() - begin.column() + 1;
569
570 for (int column = 0; column < numberOfColumnsChanged; ++column) {
571 const int columnIndex = begin.column() + column;
572 const int rowIndex = begin.row() + (columnIndex * rows());
573 m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles);
574 }
575}
576
577void QQmlTableInstanceModel::modelAboutToBeResetCallback()
578{
579 // When the model is reset, we can no longer rely on any of the data it has
580 // provided us so far. Normally it's enough for the view to recreate all the
581 // delegate items in that case, except if the model roles has changed as well
582 // (since those are cached by QQmlAdaptorModel / Accessors). For the latter case, we
583 // simply set the model once more in the delegate model to rebuild everything.
584 auto const aim = abstractItemModel();
585 auto oldRoleNames = aim->roleNames();
586 QObject::connect(aim, &QAbstractItemModel::modelReset, this, [this, aim, oldRoleNames](){
587 if (oldRoleNames != aim->roleNames()) {
588 // We refresh the model, but without sending any signals. The actual model object
589 // stays the same after all.
590 forceSetModel(model());
591 }
592 }, Qt::SingleShotConnection);
593}
594
595QQmlComponent *QQmlTableInstanceModel::delegate() const
596{
597 return m_delegate;
598}
599
600void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
601{
602 if (m_delegate == delegate)
603 return;
604
605 m_delegateChooser = nullptr;
606 if (delegate) {
607 QQmlAbstractDelegateComponent *adc =
608 qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
609 if (adc)
610 m_delegateChooser = adc;
611 }
612
613 m_delegate = delegate;
614}
615
616const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const
617{
618 return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() : nullptr;
619}
620
621// --------------------------------------------------------
622
624{
625 initializeRequiredProperties(
626 modelItemToIncubate, object, tableInstanceModel->delegateModelAccess());
627 modelItemToIncubate->setObject(object);
628
629 const bool init = true;
630 const int flatIndex = modelItemToIncubate->modelIndex();
631 emit tableInstanceModel->initItem(flatIndex, object);
632 emit tableInstanceModel->updateItemProperties(flatIndex, object, init);
633
634 if (!QQmlIncubatorPrivate::get(this)->requiredProperties()->empty())
635 modelItemToIncubate->destroyObjectLater();
636}
637
638void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status)
639{
640 if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
641 return;
642
643 // We require the view to cancel any ongoing load
644 // requests before the tableInstanceModel is destructed.
645 Q_ASSERT(tableInstanceModel);
646
647 tableInstanceModel->incubatorStatusChanged(this, status);
648}
649
650QT_END_NAMESPACE
651
652#include "moc_qqmltableinstancemodel_p.cpp"
void statusChanged(Status status) override
Called when the status of the incubator changes.
void setInitialState(QObject *object) override
Called after the object is first created, but before complex property bindings are evaluated and,...
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE const char * kModelItemTag