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);
79QQmlComponent *QQmlTableInstanceModel::resolveDelegate(
int index)
81 if (m_delegateChooser) {
82 const int row = m_adaptorModel.rowAt(index);
83 const int column = m_adaptorModel.columnAt(index);
84 QQmlComponent *delegate =
nullptr;
85 QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
87 delegate = chooser->delegate(&m_adaptorModel, row, column);
88 chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
96QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(
int index)
99 QQmlDelegateModelItem *modelItem = m_modelItems.value(index,
nullptr);
103 QQmlComponent *delegate = resolveDelegate(index);
108 modelItem = m_reusableItemsPool.takeItem(delegate, index);
110 reuseItem(modelItem, index);
111 m_modelItems.insert(index, modelItem);
116 modelItem = m_adaptorModel.createItem(m_metaType.data(), index);
118 modelItem->setDelegate(delegate);
119 m_modelItems.insert(index, modelItem);
123 qWarning() << Q_FUNC_INFO <<
"failed creating a model item for index: " << index;
127QObject *QQmlTableInstanceModel::object(
int index, QQmlIncubator::IncubationMode incubationMode)
129 Q_ASSERT(m_delegate);
130 Q_ASSERT(index >= 0 && index < m_adaptorModel.count());
132 QQmlDelegateModelItem *modelItem = resolveModelItem(index);
138 if (modelItem->object())
139 return modelItem->referenceObjectWeak();
142 incubateModelItem(modelItem, incubationMode);
143 if (!isDoneIncubating(modelItem))
147 Q_ASSERT(!modelItem->incubationTask());
150 if (modelItem->object())
151 return modelItem->referenceObjectWeak();
157 Q_ASSERT(!modelItem->isObjectReferenced());
158 Q_ASSERT(!modelItem->isScriptReferenced());
159 m_modelItems.remove(modelItem->modelIndex());
164QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable)
167 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
170 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
171 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
173 if (!modelItem->releaseObjectWeak())
174 return QQmlDelegateModel::Referenced;
176 if (modelItem->isScriptReferenced()) {
183 return QQmlDelegateModel::Destroyed;
187 m_modelItems.remove(modelItem->modelIndex());
189 if (reusable == Reusable && m_reusableItemsPool.insertItem(modelItem)) {
190 emit itemPooled(modelItem->modelIndex(), modelItem->object());
191 return QQmlInstanceModel::Pooled;
195 destroyModelItem(modelItem, Deferred);
196 return QQmlInstanceModel::Destroyed;
199void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem, DestructionMode mode)
201 emit destroyingItem(modelItem->object());
202 if (mode == Deferred)
203 modelItem->destroyObjectLater();
205 modelItem->destroyObject();
209void QQmlTableInstanceModel::dispose(QObject *object)
212 auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
215 modelItem->releaseObjectWeak();
218 Q_ASSERT(!modelItem->isObjectReferenced());
219 Q_ASSERT(!modelItem->isScriptReferenced());
221 Q_ASSERT(m_modelItems.contains(modelItem->modelIndex()));
222 Q_ASSERT(m_modelItems[modelItem->modelIndex()]->object() == object);
224 m_modelItems.remove(modelItem->modelIndex());
226 emit destroyingItem(object);
227 modelItem->destroyObject();
232void QQmlTableInstanceModel::cancel(
int index)
234 auto modelItem = m_modelItems.value(index);
240 Q_ASSERT(modelItem->incubationTask());
241 Q_ASSERT(!modelItem->isObjectReferenced());
243 m_modelItems.remove(index);
245 modelItem->destroyObject();
251void QQmlTableInstanceModel::drainReusableItemsPool(
int maxPoolTime)
253 m_reusableItemsPool.drain(maxPoolTime, [
this](QQmlDelegateModelItem *modelItem) {
254 destroyModelItem(modelItem, Immediate);
258void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item,
int newModelIndex)
265 const bool alwaysEmit =
true;
266 const int newRow = m_adaptorModel.rowAt(newModelIndex);
267 const int newColumn = m_adaptorModel.columnAt(newModelIndex);
268 item->setModelIndex(newModelIndex, newRow, newColumn, alwaysEmit);
272 auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
273 auto const updateAllRoles = QList<
int>();
274 m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
278 emit itemReused(newModelIndex, item->object());
281void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
285 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
287 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask()) {
290 const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested);
291 if (sync && incubationTask->incubationMode() == QQmlIncubator::Asynchronous)
292 incubationTask->forceCompletion();
293 }
else if (m_qmlContext && m_qmlContext->isValid()) {
294 modelItem->setIncubationTask(
295 new QQmlTableInstanceModelIncubationTask(
this, modelItem, incubationMode));
301 QQmlComponent *delegate = modelItem->delegate();
302 QQmlContext *creationContext = delegate->creationContext();
303 const QQmlRefPointer<QQmlContextData> componentContext
304 = QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data());
306 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate);
308 modelItem->setContextData(componentContext);
312 if (m_adaptorModel.hasProxyObject())
313 modelItem->initProxy();
316 modelItem->incubationTask(), delegate, m_qmlContext->engine(), componentContext,
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->setContextData(ctxt);
326 if (m_adaptorModel.hasProxyObject()) {
327 if (m_adaptorModel.delegateModelAccess == QQmlDelegateModel::ReadOnly)
328 modelItem->initProxy();
330 ctxt = modelItem->initProxy();
334 modelItem->incubationTask(), modelItem->delegate(), m_qmlContext->engine(),
335 ctxt, QQmlContextData::get(m_qmlContext));
340void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
342 QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
343 Q_ASSERT(modelItem->incubationTask());
345 modelItem->clearIncubationTask();
346 incubationTask->modelItemToIncubate =
nullptr;
348 if (status == QQmlIncubator::Ready) {
349 QObject *object = modelItem->object();
353 object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
358 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
359 emit createdItem(modelItem->modelIndex(), object);
360 }
else if (status == QQmlIncubator::Error) {
361 qWarning() <<
"Error incubating delegate:" << incubationTask->errors();
364 if (!modelItem->isScriptReferenced() && !modelItem->isObjectReferenced()) {
369 m_modelItems.remove(modelItem->modelIndex());
371 if (QObject *object = modelItem->object()) {
372 QQmlDelegateModelItem::ScriptReference scriptRef(modelItem);
373 emit destroyingItem(object);
376 Q_ASSERT(!modelItem->isScriptReferenced());
377 deleteModelItemLater(modelItem);
380 deleteIncubationTaskLater(incubationTask);
383QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(
int index) {
384 const auto modelItem = m_modelItems.value(index,
nullptr);
386 return QQmlIncubator::Null;
388 if (QQDMIncubationTask *incubationTask = modelItem->incubationTask())
389 return incubationTask->status();
393 return QQmlIncubator::Ready;
396bool QQmlTableInstanceModel::setRequiredProperty(
int index,
const QString &name,
const QVariant &value)
402 const auto modelItem = m_modelItems.value(index,
nullptr);
405 if (!modelItem->object())
407 if (!modelItem->incubationTask())
410 bool wasInRequired =
false;
411 const auto task = QQmlIncubatorPrivate::get(modelItem->incubationTask());
412 RequiredProperties *props = task->requiredProperties();
416 QQmlProperty componentProp = QQmlComponentPrivate::removePropertyFromRequired(
417 modelItem->object(), name, props, QQmlEnginePrivate::get(task->enginePriv),
420 componentProp.write(value);
421 return wasInRequired;
424QQmlDelegateModelItem *QQmlTableInstanceModel::getModelItem(
int index)
426 return m_modelItems.value(index,
nullptr);
429void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
433 Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask));
434 m_finishedIncubationTasks.append(incubationTask);
435 if (m_finishedIncubationTasks.size() == 1)
436 QTimer::singleShot(1,
this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks);
439void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
441 qDeleteAll(m_finishedIncubationTasks);
442 m_finishedIncubationTasks.clear();
445QVariant QQmlTableInstanceModel::model()
const
447 return m_adaptorModel.model();
450void QQmlTableInstanceModel::forceSetModel(
const QVariant &model)
455 drainReusableItemsPool(0);
456 if (
auto const aim = abstractItemModel()) {
457 disconnect(aim, &QAbstractItemModel::dataChanged,
this, &QQmlTableInstanceModel::dataChangedCallback);
458 disconnect(aim, &QAbstractItemModel::modelAboutToBeReset,
this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
460 m_adaptorModel.setModel(model);
461 if (
auto const aim = abstractItemModel()) {
462 connect(aim, &QAbstractItemModel::dataChanged,
this, &QQmlTableInstanceModel::dataChangedCallback);
463 connect(aim, &QAbstractItemModel::modelAboutToBeReset,
this, &QQmlTableInstanceModel::modelAboutToBeResetCallback);
467void QQmlTableInstanceModel::setModel(
const QVariant &model)
469 if (m_adaptorModel.model() == model)
472 forceSetModel(model);
477void QQmlTableInstanceModel::dataChangedCallback(
const QModelIndex &begin,
const QModelIndex &end,
const QList<
int> &roles)
482 int numberOfRowsChanged = end.row() - begin.row() + 1;
483 int numberOfColumnsChanged = end.column() - begin.column() + 1;
485 for (
int column = 0; column < numberOfColumnsChanged; ++column) {
486 const int columnIndex = begin.column() + column;
487 const int rowIndex = begin.row() + (columnIndex * rows());
488 m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles);
492void QQmlTableInstanceModel::modelAboutToBeResetCallback()
499 auto const aim = abstractItemModel();
500 auto oldRoleNames = aim->roleNames();
501 QObject::connect(aim, &QAbstractItemModel::modelReset,
this, [
this, aim, oldRoleNames](){
502 if (oldRoleNames != aim->roleNames()) {
505 forceSetModel(model());
507 }, Qt::SingleShotConnection);
510QQmlComponent *QQmlTableInstanceModel::delegate()
const
515void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
517 if (m_delegate == delegate)
520 m_delegateChooser =
nullptr;
522 QQmlAbstractDelegateComponent *adc =
523 qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
525 m_delegateChooser = adc;
528 m_delegate = delegate;
531const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel()
const
533 return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() :
nullptr;
540 initializeRequiredProperties(
542 modelItemToIncubate->setObject(object);
543 emit tableInstanceModel->initItem(modelItemToIncubate->modelIndex(), object);
545 if (!QQmlIncubatorPrivate::get(
this)->requiredProperties()->empty())
546 modelItemToIncubate->destroyObjectLater();
551 if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
563#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