8#include <QtCore/QTimer>
10#include <QtQml/private/qqmlincubator_p.h>
11#include <QtQmlModels/private/qqmlchangeset_p.h>
12#include <QtQml/private/qqmlcomponent_p.h>
18bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem)
20 if (!modelItem->incubationTask)
23 const auto status = modelItem->incubationTask->status();
24 return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error);
27void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem)
31 delete modelItem->object;
32 modelItem->object =
nullptr;
33 modelItem->contextData.reset();
34 modelItem->deleteLater();
37QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent)
38 : QQmlInstanceModel(*(
new QObjectPrivate()), parent)
39 , m_qmlContext(qmlContext)
40 , m_metaType(QQml::makeRefPointer<QQmlDelegateModelItemMetaType>(
41 m_qmlContext->engine()->handle(),
this))
45void QQmlTableInstanceModel::useImportVersion(QTypeRevision version)
47 m_adaptorModel.useImportVersion(version);
50QQmlTableInstanceModel::~QQmlTableInstanceModel()
52 for (
const auto modelItem : m_modelItems) {
56 Q_ASSERT(modelItem->objectRef == 0);
57 Q_ASSERT(modelItem->incubationTask);
60 Q_ASSERT(modelItem->scriptRef == 0);
62 if (modelItem->object) {
63 delete modelItem->object;
64 modelItem->object =
nullptr;
65 modelItem->contextData.reset();
69 deleteAllFinishedIncubationTasks();
70 qDeleteAll(m_modelItems);
71 drainReusableItemsPool(0);
74QQmlComponent *QQmlTableInstanceModel::resolveDelegate(
int index)
76 if (m_delegateChooser) {
77 const int row = m_adaptorModel.rowAt(index);
78 const int column = m_adaptorModel.columnAt(index);
79 QQmlComponent *delegate =
nullptr;
80 QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
82 delegate = chooser->delegate(&m_adaptorModel, row, column);
83 chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
91QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(
int index)
94 QQmlDelegateModelItem *modelItem = m_modelItems.value(index,
nullptr);
98 QQmlComponent *delegate = resolveDelegate(index);
103 modelItem = m_reusableItemsPool.takeItem(delegate, index);
105 reuseItem(modelItem, index);
106 m_modelItems.insert(index, modelItem);
111 modelItem = m_adaptorModel.createItem(m_metaType.data(), index);
113 modelItem->delegate = delegate;
114 m_modelItems.insert(index, modelItem);
118 qWarning() << Q_FUNC_INFO <<
"failed creating a model item for index: " << index;
122QObject *QQmlTableInstanceModel::object(
int index, QQmlIncubator::IncubationMode incubationMode)
124 Q_ASSERT(m_delegate);
125 Q_ASSERT(index >= 0 && index < m_adaptorModel.count());
127 QQmlDelegateModelItem *modelItem = resolveModelItem(index);
131 if (modelItem->object) {
134 modelItem->referenceObject();
135 return modelItem->object;
139 incubateModelItem(modelItem, incubationMode);
140 if (!isDoneIncubating(modelItem))
144 Q_ASSERT(!modelItem->incubationTask);
146 if (!modelItem->object) {
151 Q_ASSERT(!modelItem->isObjectReferenced());
152 Q_ASSERT(!modelItem->isReferenced());
153 m_modelItems.remove(modelItem->modelIndex());
159 modelItem->referenceObject();
160 return modelItem->object;
163QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable)
166 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
169 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
170 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object == object);
172 if (!modelItem->releaseObject())
173 return QQmlDelegateModel::Referenced;
175 if (modelItem->isReferenced()) {
182 return QQmlDelegateModel::Destroyed;
186 m_modelItems.remove(modelItem->modelIndex());
188 if (reusable == Reusable && m_reusableItemsPool.insertItem(modelItem)) {
189 emit itemPooled(modelItem->modelIndex(), modelItem->object);
190 return QQmlInstanceModel::Pooled;
194 destroyModelItem(modelItem, Deferred);
195 return QQmlInstanceModel::Destroyed;
198void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode)
200 emit destroyingItem(modelItem->object);
201 if (mode == Deferred)
202 modelItem->destroyObject();
204 delete modelItem->object;
208void QQmlTableInstanceModel::dispose(QObject *object)
211 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
214 modelItem->releaseObject();
217 Q_ASSERT(!modelItem->isObjectReferenced());
218 Q_ASSERT(!modelItem->isReferenced());
220 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
221 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object == object);
223 m_modelItems.remove(modelItem->modelIndex());
225 emit destroyingItem(object);
230void QQmlTableInstanceModel::cancel(
int index)
232 auto modelItem = m_modelItems.value(index);
238 Q_ASSERT(modelItem->incubationTask);
239 Q_ASSERT(!modelItem->isObjectReferenced());
241 m_modelItems.remove(index);
243 if (modelItem->object)
244 delete modelItem->object;
250void QQmlTableInstanceModel::drainReusableItemsPool(
int maxPoolTime)
252 m_reusableItemsPool.drain(maxPoolTime, [
this](QQmlDelegateModelItem *modelItem) {
253 destroyModelItem(modelItem, Immediate);
257void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item,
int newModelIndex)
264 const bool alwaysEmit =
true;
265 const int newRow = m_adaptorModel.rowAt(newModelIndex);
266 const int newColumn = m_adaptorModel.columnAt(newModelIndex);
267 item->setModelIndex(newModelIndex, newRow, newColumn, alwaysEmit);
271 auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
272 auto const updateAllRoles = QVector<
int>();
273 m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
277 emit itemReused(newModelIndex, item->object);
280void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
284 modelItem->scriptRef++;
286 if (modelItem->incubationTask) {
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);
299 QQmlContext *creationContext = modelItem->delegate->creationContext();
300 const QQmlRefPointer<QQmlContextData> componentContext
301 = QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data());
303 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(modelItem->delegate);
305 modelItem->contextData = componentContext;
309 if (m_adaptorModel.hasProxyObject())
310 modelItem->initProxy();
313 modelItem->incubationTask,
315 m_qmlContext->engine(),
317 QQmlContextData::get(m_qmlContext));
319 QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted(
320 QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()));
321 ctxt->setContextObject(modelItem);
322 modelItem->contextData = ctxt;
326 if (m_adaptorModel.hasProxyObject()) {
327 if (m_adaptorModel.delegateModelAccess == QQmlDelegateModel::ReadOnly)
328 modelItem->initProxy();
330 ctxt = modelItem->initProxy();
334 modelItem->incubationTask,
336 m_qmlContext->engine(),
338 QQmlContextData::get(m_qmlContext));
343 modelItem->scriptRef--;
346void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
348 QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
349 Q_ASSERT(modelItem->incubationTask);
351 modelItem->incubationTask =
nullptr;
352 incubationTask->modelItemToIncubate =
nullptr;
354 if (status == QQmlIncubator::Ready) {
356 modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
361 Q_ASSERT(modelItem->object);
362 modelItem->scriptRef++;
363 emit createdItem(modelItem->modelIndex(), modelItem->object);
364 modelItem->scriptRef--;
365 }
else if (status == QQmlIncubator::Error) {
366 qWarning() <<
"Error incubating delegate:" << incubationTask->errors();
369 if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) {
374 m_modelItems.remove(modelItem->modelIndex());
376 if (modelItem->object) {
377 modelItem->scriptRef++;
378 emit destroyingItem(modelItem->object);
379 modelItem->scriptRef--;
380 Q_ASSERT(!modelItem->isReferenced());
383 deleteModelItemLater(modelItem);
386 deleteIncubationTaskLater(incubationTask);
389QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(
int index) {
390 const auto modelItem = m_modelItems.value(index,
nullptr);
392 return QQmlIncubator::Null;
394 if (modelItem->incubationTask)
395 return modelItem->incubationTask->status();
399 return QQmlIncubator::Ready;
402bool QQmlTableInstanceModel::setRequiredProperty(
int index,
const QString &name,
const QVariant &value)
408 const auto modelItem = m_modelItems.value(index,
nullptr);
411 if (!modelItem->object)
413 if (!modelItem->incubationTask)
416 bool wasInRequired =
false;
417 const auto task = QQmlIncubatorPrivate::get(modelItem->incubationTask);
418 RequiredProperties *props = task->requiredProperties();
422 QQmlProperty componentProp = QQmlComponentPrivate::removePropertyFromRequired(
423 modelItem->object, name, props, QQmlEnginePrivate::get(task->enginePriv),
426 componentProp.write(value);
427 return wasInRequired;
430QQmlDelegateModelItem *QQmlTableInstanceModel::getModelItem(
int index)
432 return m_modelItems.value(index,
nullptr);
435void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
439 Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask));
440 m_finishedIncubationTasks.append(incubationTask);
441 if (m_finishedIncubationTasks.size() == 1)
442 QTimer::singleShot(1,
this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks);
445void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
447 qDeleteAll(m_finishedIncubationTasks);
448 m_finishedIncubationTasks.clear();
451QVariant QQmlTableInstanceModel::model()
const
453 return m_adaptorModel.model();
456void QQmlTableInstanceModel::forceSetModel(
const QVariant &model)
461 drainReusableItemsPool(0);
462 if (
auto const aim = abstractItemModel()) {
463 disconnect(aim, &QAbstractItemModel::dataChanged,
this, &QQmlTableInstanceModel::dataChangedCallback);
464 disconnect(aim, &QAbstractItemModel::modelAboutToBeReset,
this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
466 m_adaptorModel.setModel(model);
467 if (
auto const aim = abstractItemModel()) {
468 connect(aim, &QAbstractItemModel::dataChanged,
this, &QQmlTableInstanceModel::dataChangedCallback);
469 connect(aim, &QAbstractItemModel::modelAboutToBeReset,
this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
473void QQmlTableInstanceModel::setModel(
const QVariant &model)
475 if (m_adaptorModel.model() == model)
478 forceSetModel(model);
483void QQmlTableInstanceModel::dataChangedCallback(
const QModelIndex &begin,
const QModelIndex &end,
const QVector<
int> &roles)
488 int numberOfRowsChanged = end.row() - begin.row() + 1;
489 int numberOfColumnsChanged = end.column() - begin.column() + 1;
491 for (
int column = 0; column < numberOfColumnsChanged; ++column) {
492 const int columnIndex = begin.column() + column;
493 const int rowIndex = begin.row() + (columnIndex * rows());
494 m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles);
498void QQmlTableInstanceModel::modelAboutToBeResetCallback()
505 auto const aim = abstractItemModel();
506 auto oldRoleNames = aim->roleNames();
507 QObject::connect(aim, &QAbstractItemModel::modelReset,
this, [
this, aim, oldRoleNames](){
508 if (oldRoleNames != aim->roleNames()) {
511 forceSetModel(model());
513 }, Qt::SingleShotConnection);
516QQmlComponent *QQmlTableInstanceModel::delegate()
const
521void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
523 if (m_delegate == delegate)
526 m_delegateChooser =
nullptr;
528 QQmlAbstractDelegateComponent *adc =
529 qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
531 m_delegateChooser = adc;
534 m_delegate = delegate;
537const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel()
const
539 return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() :
nullptr;
546 initializeRequiredProperties(
547 modelItemToIncubate, object, tableInstanceModel->delegateModelAccess());
548 modelItemToIncubate->object = object;
549 emit tableInstanceModel->initItem(modelItemToIncubate->modelIndex(), object);
551 if (!QQmlIncubatorPrivate::get(
this)->requiredProperties()->empty()) {
552 modelItemToIncubate->object =
nullptr;
553 object->deleteLater();
559 if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
571#include "moc_qqmltableinstancemodel_p.cpp"
void statusChanged(Status status) override
Called when the status of the incubator changes.
QQmlTableInstanceModel * tableInstanceModel
void setInitialState(QObject *object) override
Called after the object is first created, but before complex property bindings are evaluated and,...
QT_BEGIN_NAMESPACE const char * kModelItemTag