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>
14#include <QtCore/qtyperevision.h>
20bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem)
22 QQDMIncubationTask *incubationTask = modelItem->incubationTask();
26 switch (incubationTask->status()) {
27 case QQmlIncubator::Ready:
28 case QQmlIncubator::Error:
37void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem)
41 modelItem->destroyObject();
42 modelItem->deleteLater();
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))
53void QQmlTableInstanceModel::useImportVersion(QTypeRevision version)
55 m_adaptorModel.useImportVersion(version);
58QQmlTableInstanceModel::~QQmlTableInstanceModel()
60 for (
const auto modelItem : m_modelItems) {
65 Q_ASSERT(modelItem->objectStrongRef() == 0);
66 Q_ASSERT(modelItem->incubationTask());
69 Q_ASSERT(modelItem->scriptRef() == 0);
71 modelItem->destroyObject();
74 deleteAllFinishedIncubationTasks();
75 qDeleteAll(m_modelItems);
76 drainReusableItemsPool(0);
81 Q_ASSERT(m_releasedItems.isEmpty());
84QQmlComponent *QQmlTableInstanceModel::resolveDelegate(
int index)
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;
92 delegate = chooser->delegate(&m_adaptorModel, row, column);
93 chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
101QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(
int flatIndex,
const QModelIndex &modelIndex)
103 Q_ASSERT(flatIndex >= 0 && flatIndex < m_adaptorModel.count());
106 if (QQmlDelegateModelItem *modelItem = m_modelItems.value(flatIndex,
nullptr))
109 QQmlComponent *delegate = resolveDelegate(flatIndex);
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;
120 if (it != m_releasedItems.cend()) {
121 QQmlDelegateModelItem *modelItem = *it;
124 if (modelItem->delegate() == delegate) {
125 m_releasedItems.erase(it);
126 restoreFromReleasedItemsCache(modelItem, flatIndex);
133 if (QQmlDelegateModelItem *modelItem = m_reusableItemsPool.takeItem(delegate, flatIndex)) {
134 m_modelItems.insert(flatIndex, modelItem);
135 reuseItem(modelItem, flatIndex);
140 if (QQmlDelegateModelItem *modelItem = m_adaptorModel.createItem(m_metaType.data(), flatIndex)) {
141 modelItem->setDelegate(delegate);
142 m_modelItems.insert(flatIndex, modelItem);
146 qWarning() << Q_FUNC_INFO <<
"failed creating a model item for index: " << flatIndex << modelIndex;
150void QQmlTableInstanceModel::commitReleasedItems()
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());
159 destroyModelItem(item, Deferred);
161 m_releasedItems.clear();
164QObject *QQmlTableInstanceModel::object(
int index, QQmlIncubator::IncubationMode incubationMode)
166 Q_ASSERT(m_delegate);
168 QModelIndex modelIndex;
169 if (
const QAbstractItemModel *aim = abstractItemModel()) {
172 const int row = m_adaptorModel.rowAt(index);
173 const int column = m_adaptorModel.columnAt(index);
174 modelIndex = aim->index(row, column);
177 if (QQmlDelegateModelItem *modelItem = resolveModelItem(index, modelIndex)) {
179 return incubateModelItemIfNeeded(modelItem, incubationMode);
185QObject *QQmlTableInstanceModel::incubateModelItemIfNeeded(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
187 if (modelItem->object()) {
190 return modelItem->referenceObjectWeak();
194 incubateModelItem(modelItem, incubationMode);
195 if (!isDoneIncubating(modelItem))
199 Q_ASSERT(!modelItem->incubationTask());
202 if (modelItem->object())
203 return modelItem->referenceObjectWeak();
209 Q_ASSERT(!modelItem->isObjectReferenced());
210 Q_ASSERT(!modelItem->isScriptReferenced());
211 m_modelItems.remove(modelItem->modelIndex());
216QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable)
219 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
222 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
223 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
225 if (!modelItem->releaseObjectWeak())
226 return QQmlDelegateModel::Referenced;
228 if (modelItem->isScriptReferenced()) {
235 return QQmlDelegateModel::Destroyed;
239 m_modelItems.remove(modelItem->modelIndex());
241 if (reusable == Reusable) {
249 m_releasedItems.append(modelItem);
250 return QQmlInstanceModel::Pooled;
254 destroyModelItem(modelItem, Deferred);
255 return QQmlInstanceModel::Destroyed;
258void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode)
260 emit destroyingItem(modelItem->object());
261 if (mode == Deferred)
262 modelItem->destroyObjectLater();
264 modelItem->destroyObject();
268void QQmlTableInstanceModel::dispose(QObject *object)
271 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
274 modelItem->releaseObjectWeak();
277 Q_ASSERT(!modelItem->isObjectReferenced());
278 Q_ASSERT(!modelItem->isScriptReferenced());
280 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
281 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
283 m_modelItems.remove(modelItem->modelIndex());
285 emit destroyingItem(object);
286 modelItem->destroyObject();
291void QQmlTableInstanceModel::cancel(
int index)
293 auto modelItem = m_modelItems.value(index);
299 Q_ASSERT(modelItem->incubationTask());
300 Q_ASSERT(!modelItem->isObjectReferenced());
302 m_modelItems.remove(index);
304 modelItem->destroyObject();
310void QQmlTableInstanceModel::drainReusableItemsPool(
int maxPoolTime)
312 m_reusableItemsPool.drain(maxPoolTime, [
this](QQmlDelegateModelItem *modelItem) {
313 destroyModelItem(modelItem, Immediate);
317void QQmlTableInstanceModel::restoreFromReleasedItemsCache(QQmlDelegateModelItem *item,
int newFlatIndex)
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);
332void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item,
int newModelIndex)
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));
348 auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
349 auto const updateAllRoles = QList<
int>();
350 m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
354 const bool init =
false;
355 emit updateItemProperties(newModelIndex, item->object(), init);
356 emit itemReused(newModelIndex, item->object());
359void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
363 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
365 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask()) {
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));
379 QQmlComponent *delegate = modelItem->delegate();
380 QQmlContext *creationContext = delegate->creationContext();
381 const QQmlRefPointer<QQmlContextData> componentContext
382 = QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data());
384 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate);
386 modelItem->setContextData(componentContext);
390 if (m_adaptorModel.hasProxyObject())
391 modelItem->initProxy();
394 modelItem->incubationTask(), delegate, m_qmlContext->engine(), componentContext,
395 QQmlContextData::get(m_qmlContext));
397 QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted(
398 QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()));
399 ctxt->setContextObject(modelItem);
400 modelItem->setContextData(ctxt);
404 if (m_adaptorModel.hasProxyObject()) {
405 if (m_adaptorModel.delegateModelAccess == QQmlDelegateModel::ReadOnly)
406 modelItem->initProxy();
408 ctxt = modelItem->initProxy();
412 modelItem->incubationTask(), modelItem->delegate(), m_qmlContext->engine(),
413 ctxt, QQmlContextData::get(m_qmlContext));
418void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
420 QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
421 Q_ASSERT(modelItem->incubationTask());
423 modelItem->clearIncubationTask();
424 incubationTask->modelItemToIncubate =
nullptr;
426 if (status == QQmlIncubator::Ready) {
427 QObject *object = modelItem->object();
430 if (
auto *qaim = abstractItemModel()) {
434 modelItem->setPersistentModelIndex(qaim->index(modelItem->modelRow(), modelItem->modelColumn()));
438 object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
443 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
444 emit createdItem(modelItem->modelIndex(), object);
445 }
else if (status == QQmlIncubator::Error) {
446 qWarning() <<
"Error incubating delegate:" << incubationTask->errors();
449 if (!modelItem->isScriptReferenced() && !modelItem->isObjectReferenced()) {
454 m_modelItems.remove(modelItem->modelIndex());
456 if (QObject *object = modelItem->object()) {
457 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
458 emit destroyingItem(object);
461 Q_ASSERT(!modelItem->isScriptReferenced());
462 deleteModelItemLater(modelItem);
465 deleteIncubationTaskLater(incubationTask);
468QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(
int index) {
469 const auto modelItem = m_modelItems.value(index,
nullptr);
471 return QQmlIncubator::Null;
473 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask())
474 return incubationTask->status();
478 return QQmlIncubator::Ready;
481bool QQmlTableInstanceModel::setRequiredProperty(
int index,
const QString &name,
const QVariant &value)
487 const auto modelItem = m_modelItems.value(index,
nullptr);
490 if (!modelItem->object())
492 if (!modelItem->incubationTask())
495 bool wasInRequired =
false;
496 const auto task = QQmlIncubatorPrivate::get(modelItem->incubationTask());
497 RequiredProperties *props = task->requiredProperties();
501 QQmlProperty componentProp = QQmlComponentPrivate::removePropertyFromRequired(
502 modelItem->object(), name, props, QQmlEnginePrivate::get(task->enginePriv),
505 componentProp.write(value);
506 return wasInRequired;
509QQmlDelegateModelItem *QQmlTableInstanceModel::getModelItem(
int index)
511 return m_modelItems.value(index,
nullptr);
514void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
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);
524void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
526 qDeleteAll(m_finishedIncubationTasks);
527 m_finishedIncubationTasks.clear();
530QVariant QQmlTableInstanceModel::model()
const
532 return m_adaptorModel.model();
535void QQmlTableInstanceModel::forceSetModel(
const QVariant &model)
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);
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);
552void QQmlTableInstanceModel::setModel(
const QVariant &model)
554 if (m_adaptorModel.model() == model)
557 forceSetModel(model);
562void QQmlTableInstanceModel::dataChangedCallback(
const QModelIndex &begin,
const QModelIndex &end,
const QList<
int> &roles)
567 int numberOfRowsChanged = end.row() - begin.row() + 1;
568 int numberOfColumnsChanged = end.column() - begin.column() + 1;
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);
577void QQmlTableInstanceModel::modelAboutToBeResetCallback()
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()) {
590 forceSetModel(model());
592 }, Qt::SingleShotConnection);
595QQmlComponent *QQmlTableInstanceModel::delegate()
const
600void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
602 if (m_delegate == delegate)
605 m_delegateChooser =
nullptr;
607 QQmlAbstractDelegateComponent *adc =
608 qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
610 m_delegateChooser = adc;
613 m_delegate = delegate;
616const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel()
const
618 return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() :
nullptr;
625 initializeRequiredProperties(
627 modelItemToIncubate->setObject(object);
629 const bool init =
true;
630 const int flatIndex = modelItemToIncubate->modelIndex();
631 emit tableInstanceModel->initItem(flatIndex, object);
632 emit tableInstanceModel->updateItemProperties(flatIndex, object, init);
634 if (!QQmlIncubatorPrivate::get(
this)->requiredProperties()->empty())
635 modelItemToIncubate->destroyObjectLater();
640 if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
652#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,...
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE const char * kModelItemTag