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 QQDMIncubationTask *incubationTask = modelItem->incubationTask();
24 switch (incubationTask->status()) {
25 case QQmlIncubator::Ready:
26 case QQmlIncubator::Error:
35void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem)
39 modelItem->destroyObject();
40 modelItem->deleteLater();
43QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent)
44 : QQmlInstanceModel(*(
new QObjectPrivate()), parent)
45 , m_qmlContext(qmlContext)
46 , m_metaType(QQml::makeRefPointer<QQmlDelegateModelItemMetaType>(
47 m_qmlContext->engine()->handle(),
this))
51void QQmlTableInstanceModel::useImportVersion(QTypeRevision version)
53 m_adaptorModel.useImportVersion(version);
56QQmlTableInstanceModel::~QQmlTableInstanceModel()
58 for (
const auto modelItem : m_modelItems) {
63 Q_ASSERT(modelItem->objectStrongRef() == 0);
64 Q_ASSERT(modelItem->incubationTask());
67 Q_ASSERT(modelItem->scriptRef() == 0);
69 modelItem->destroyObject();
72 deleteAllFinishedIncubationTasks();
73 qDeleteAll(m_modelItems);
74 drainReusableItemsPool(0);
77QQmlComponent *QQmlTableInstanceModel::resolveDelegate(
int index)
79 if (m_delegateChooser) {
80 const int row = m_adaptorModel.rowAt(index);
81 const int column = m_adaptorModel.columnAt(index);
82 QQmlComponent *delegate =
nullptr;
83 QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
85 delegate = chooser->delegate(&m_adaptorModel, row, column);
86 chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
94QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(
int index)
97 QQmlDelegateModelItem *modelItem = m_modelItems.value(index,
nullptr);
101 QQmlComponent *delegate = resolveDelegate(index);
106 modelItem = m_reusableItemsPool.takeItem(delegate, index);
108 reuseItem(modelItem, index);
109 m_modelItems.insert(index, modelItem);
114 modelItem = m_adaptorModel.createItem(m_metaType.data(), index);
116 modelItem->setDelegate(delegate);
117 m_modelItems.insert(index, modelItem);
121 qWarning() << Q_FUNC_INFO <<
"failed creating a model item for index: " << index;
125QObject *QQmlTableInstanceModel::object(
int index, QQmlIncubator::IncubationMode incubationMode)
127 Q_ASSERT(m_delegate);
128 Q_ASSERT(index >= 0 && index < m_adaptorModel.count());
130 QQmlDelegateModelItem *modelItem = resolveModelItem(index);
136 if (modelItem->object())
137 return modelItem->referenceObjectWeak();
140 incubateModelItem(modelItem, incubationMode);
141 if (!isDoneIncubating(modelItem))
145 Q_ASSERT(!modelItem->incubationTask());
148 if (modelItem->object())
149 return modelItem->referenceObjectWeak();
155 Q_ASSERT(!modelItem->isObjectReferenced());
156 Q_ASSERT(!modelItem->isScriptReferenced());
157 m_modelItems.remove(modelItem->modelIndex());
162QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable)
165 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
168 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
169 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
171 if (!modelItem->releaseObjectWeak())
172 return QQmlDelegateModel::Referenced;
174 if (modelItem->isScriptReferenced()) {
181 return QQmlDelegateModel::Destroyed;
185 m_modelItems.remove(modelItem->modelIndex());
187 if (reusable == Reusable && m_reusableItemsPool.insertItem(modelItem)) {
188 emit itemPooled(modelItem->modelIndex(), modelItem->object());
189 return QQmlInstanceModel::Pooled;
193 destroyModelItem(modelItem, Deferred);
194 return QQmlInstanceModel::Destroyed;
197void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode)
199 emit destroyingItem(modelItem->object());
200 if (mode == Deferred)
201 modelItem->destroyObjectLater();
203 modelItem->destroyObject();
207void QQmlTableInstanceModel::dispose(QObject *object)
210 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
213 modelItem->releaseObjectWeak();
216 Q_ASSERT(!modelItem->isObjectReferenced());
217 Q_ASSERT(!modelItem->isScriptReferenced());
219 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
220 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
222 m_modelItems.remove(modelItem->modelIndex());
224 emit destroyingItem(object);
225 modelItem->destroyObject();
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 modelItem->destroyObject();
249void QQmlTableInstanceModel::drainReusableItemsPool(
int maxPoolTime)
251 m_reusableItemsPool.drain(maxPoolTime, [
this](QQmlDelegateModelItem *modelItem) {
252 destroyModelItem(modelItem, Immediate);
256void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item,
int newModelIndex)
263 const bool alwaysEmit =
true;
264 const int newRow = m_adaptorModel.rowAt(newModelIndex);
265 const int newColumn = m_adaptorModel.columnAt(newModelIndex);
266 item->setModelIndex(newModelIndex, newRow, newColumn, alwaysEmit);
270 auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
271 auto const updateAllRoles = QList<
int>();
272 m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
276 emit itemReused(newModelIndex, item->object());
279void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
283 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
285 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask()) {
288 const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested);
289 if (sync && incubationTask->incubationMode() == QQmlIncubator::Asynchronous)
290 incubationTask->forceCompletion();
291 }
else if (m_qmlContext && m_qmlContext->isValid()) {
292 modelItem->setIncubationTask(
293 new QQmlTableInstanceModelIncubationTask(
this, modelItem, incubationMode));
299 QQmlComponent *delegate = modelItem->delegate();
300 QQmlContext *creationContext = delegate->creationContext();
301 const QQmlRefPointer<QQmlContextData> componentContext
302 = QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data());
304 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate);
306 modelItem->setContextData(componentContext);
310 if (m_adaptorModel.hasProxyObject())
311 modelItem->initProxy();
314 modelItem->incubationTask(), delegate, m_qmlContext->engine(), componentContext,
315 QQmlContextData::get(m_qmlContext));
317 QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted(
318 QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()));
319 ctxt->setContextObject(modelItem);
320 modelItem->setContextData(ctxt);
324 if (m_adaptorModel.hasProxyObject()) {
325 if (m_adaptorModel.delegateModelAccess == QQmlDelegateModel::ReadOnly)
326 modelItem->initProxy();
328 ctxt = modelItem->initProxy();
332 modelItem->incubationTask(), modelItem->delegate(), m_qmlContext->engine(),
333 ctxt, QQmlContextData::get(m_qmlContext));
338void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
340 QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
341 Q_ASSERT(modelItem->incubationTask());
343 modelItem->clearIncubationTask();
344 incubationTask->modelItemToIncubate =
nullptr;
346 if (status == QQmlIncubator::Ready) {
347 QObject *object = modelItem->object();
351 object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
356 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
357 emit createdItem(modelItem->modelIndex(), object);
358 }
else if (status == QQmlIncubator::Error) {
359 qWarning() <<
"Error incubating delegate:" << incubationTask->errors();
362 if (!modelItem->isScriptReferenced() && !modelItem->isObjectReferenced()) {
367 m_modelItems.remove(modelItem->modelIndex());
369 if (QObject *object = modelItem->object()) {
370 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
371 emit destroyingItem(object);
374 Q_ASSERT(!modelItem->isScriptReferenced());
375 deleteModelItemLater(modelItem);
378 deleteIncubationTaskLater(incubationTask);
381QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(
int index) {
382 const auto modelItem = m_modelItems.value(index,
nullptr);
384 return QQmlIncubator::Null;
386 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask())
387 return incubationTask->status();
391 return QQmlIncubator::Ready;
394bool QQmlTableInstanceModel::setRequiredProperty(
int index,
const QString &name,
const QVariant &value)
400 const auto modelItem = m_modelItems.value(index,
nullptr);
403 if (!modelItem->object())
405 if (!modelItem->incubationTask())
408 bool wasInRequired =
false;
409 const auto task = QQmlIncubatorPrivate::get(modelItem->incubationTask());
410 RequiredProperties *props = task->requiredProperties();
414 QQmlProperty componentProp = QQmlComponentPrivate::removePropertyFromRequired(
415 modelItem->object(), name, props, QQmlEnginePrivate::get(task->enginePriv),
418 componentProp.write(value);
419 return wasInRequired;
422QQmlDelegateModelItem *QQmlTableInstanceModel::getModelItem(
int index)
424 return m_modelItems.value(index,
nullptr);
427void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
431 Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask));
432 m_finishedIncubationTasks.append(incubationTask);
433 if (m_finishedIncubationTasks.size() == 1)
434 QTimer::singleShot(1,
this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks);
437void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
439 qDeleteAll(m_finishedIncubationTasks);
440 m_finishedIncubationTasks.clear();
443QVariant QQmlTableInstanceModel::model()
const
445 return m_adaptorModel.model();
448void QQmlTableInstanceModel::forceSetModel(
const QVariant &model)
453 drainReusableItemsPool(0);
454 if (
auto const aim = abstractItemModel()) {
455 disconnect(aim, &QAbstractItemModel::dataChanged,
this, &QQmlTableInstanceModel::dataChangedCallback);
456 disconnect(aim, &QAbstractItemModel::modelAboutToBeReset,
this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
458 m_adaptorModel.setModel(model);
459 if (
auto const aim = abstractItemModel()) {
460 connect(aim, &QAbstractItemModel::dataChanged,
this, &QQmlTableInstanceModel::dataChangedCallback);
461 connect(aim, &QAbstractItemModel::modelAboutToBeReset,
this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
465void QQmlTableInstanceModel::setModel(
const QVariant &model)
467 if (m_adaptorModel.model() == model)
470 forceSetModel(model);
475void QQmlTableInstanceModel::dataChangedCallback(
const QModelIndex &begin,
const QModelIndex &end,
const QList<
int> &roles)
480 int numberOfRowsChanged = end.row() - begin.row() + 1;
481 int numberOfColumnsChanged = end.column() - begin.column() + 1;
483 for (
int column = 0; column < numberOfColumnsChanged; ++column) {
484 const int columnIndex = begin.column() + column;
485 const int rowIndex = begin.row() + (columnIndex * rows());
486 m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles);
490void QQmlTableInstanceModel::modelAboutToBeResetCallback()
497 auto const aim = abstractItemModel();
498 auto oldRoleNames = aim->roleNames();
499 QObject::connect(aim, &QAbstractItemModel::modelReset,
this, [
this, aim, oldRoleNames](){
500 if (oldRoleNames != aim->roleNames()) {
503 forceSetModel(model());
505 }, Qt::SingleShotConnection);
508QQmlComponent *QQmlTableInstanceModel::delegate()
const
513void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
515 if (m_delegate == delegate)
518 m_delegateChooser =
nullptr;
520 QQmlAbstractDelegateComponent *adc =
521 qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
523 m_delegateChooser = adc;
526 m_delegate = delegate;
529const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel()
const
531 return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() :
nullptr;
538 initializeRequiredProperties(
540 modelItemToIncubate->setObject(object);
541 emit tableInstanceModel->initItem(modelItemToIncubate->modelIndex(), object);
543 if (!QQmlIncubatorPrivate::get(
this)->requiredProperties()->empty())
544 modelItemToIncubate->destroyObjectLater();
549 if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
561#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