diff options
Diffstat (limited to 'src/qml/types')
-rw-r--r-- | src/qml/types/qqmlconnections.cpp | 13 | ||||
-rw-r--r-- | src/qml/types/qqmlconnections_p.h | 4 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel.cpp | 309 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel_p.h | 3 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel_p_p.h | 25 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel.cpp | 211 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel_p.h | 12 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel_p_p.h | 40 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodelworkeragent_p.h | 2 | ||||
-rw-r--r-- | src/qml/types/qqmlmodelsmodule.cpp | 4 | ||||
-rw-r--r-- | src/qml/types/qqmlobjectmodel_p.h | 2 | ||||
-rw-r--r-- | src/qml/types/qqmltableinstancemodel.cpp | 341 | ||||
-rw-r--r-- | src/qml/types/qqmltableinstancemodel_p.h | 132 | ||||
-rw-r--r-- | src/qml/types/qqmltimer_p.h | 2 | ||||
-rw-r--r-- | src/qml/types/qquickworkerscript.cpp | 12 | ||||
-rw-r--r-- | src/qml/types/types.pri | 24 |
16 files changed, 949 insertions, 187 deletions
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 183bb1bf74..f601087690 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -231,11 +231,11 @@ void QQmlConnections::setIgnoreUnknownSignals(bool ignore) d->ignoreUnknownSignals = ignore; } -void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) +void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) { for (int ii = 0; ii < props.count(); ++ii) { const QV4::CompiledData::Binding *binding = props.at(ii); - const QString &propName = qmlUnit->stringAt(binding->propertyNameIndex); + const QString &propName = compilationUnit->stringAt(binding->propertyNameIndex); if (!propName.startsWith(QLatin1String("on")) || (propName.length() < 3 || !propName.at(2).isUpper())) { error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); @@ -243,8 +243,8 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni } if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - const QV4::CompiledData::Object *target = qmlUnit->objectAt(binding->value.objectIndex); - if (!qmlUnit->stringAt(target->inheritedTypeNameIndex).isEmpty()) + const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex); + if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty()) error(binding, QQmlConnections::tr("Connections: nested objects not allowed")); else error(binding, QQmlConnections::tr("Connections: syntax error")); @@ -256,7 +256,7 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni } } -void QQmlConnectionsParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlConnectionsPrivate *p = static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object)); @@ -276,10 +276,9 @@ void QQmlConnections::connectSignals() QQmlData *ddata = QQmlData::get(this); QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr; - const QV4::CompiledData::Unit *qmlUnit = d->compilationUnit->data; for (const QV4::CompiledData::Binding *binding : qAsConst(d->bindings)) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); - QString propName = qmlUnit->stringAt(binding->propertyNameIndex); + QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex); QQmlProperty prop(target, propName); if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index 50e2c59ac3..6b063d5602 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -98,8 +98,8 @@ private: class QQmlConnectionsParser : public QQmlCustomParser { public: - void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) override; - void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; + void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override; + void applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; }; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 7a12813f0c..7a128d7070 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -93,10 +93,10 @@ struct DelegateModelGroupFunction : QV4::FunctionObject static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) { - return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code); + return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code); } - static ReturnedValue call(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) + static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) { QV4::Scope scope(that->engine()); QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); @@ -200,8 +200,7 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) */ QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) - : m_delegate(nullptr) - , m_cacheMetaType(nullptr) + : m_cacheMetaType(nullptr) , m_context(ctxt) , m_parts(nullptr) , m_filterGroup(QStringLiteral("items")) @@ -228,6 +227,14 @@ QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() m_cacheMetaType->release(); } +int QQmlDelegateModelPrivate::adaptorModelCount() const +{ + // QQmlDelegateModel currently only support list models. + // So even if a model is a table model, only the first + // column will be used. + return m_adaptorModel.rowCount(); +} + void QQmlDelegateModelPrivate::requestMoreIfNecessary() { Q_Q(QQmlDelegateModel); @@ -263,6 +270,8 @@ QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) QQmlDelegateModel::~QQmlDelegateModel() { Q_D(QQmlDelegateModel); + d->disconnectFromAbstractItemModel(); + d->m_adaptorModel.setObject(nullptr, this); for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) { if (cacheItem->object) { @@ -336,7 +345,7 @@ void QQmlDelegateModel::componentComplete() static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup(); QVector<Compositor::Insert> inserts; - d->m_count = d->m_adaptorModel.count(); + d->m_count = d->adaptorModelCount(); d->m_compositor.append( &d->m_adaptorModel, 0, @@ -368,6 +377,54 @@ QVariant QQmlDelegateModel::model() const return d->m_adaptorModel.model(); } +void QQmlDelegateModelPrivate::connectToAbstractItemModel() +{ + Q_Q(QQmlDelegateModel); + if (!m_adaptorModel.adaptsAim()) + return; + + auto aim = m_adaptorModel.aim(); + + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), + q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()), + q, QQmlDelegateModel, SLOT(_q_modelReset())); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), + q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); +} + +void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() +{ + Q_Q(QQmlDelegateModel); + if (!m_adaptorModel.adaptsAim()) + return; + + auto aim = m_adaptorModel.aim(); + + QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), + q, SLOT(_q_rowsInserted(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), + q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::disconnect(aim, SIGNAL(modelReset()), + q, SLOT(_q_modelReset())); + QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), + q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); +} + void QQmlDelegateModel::setModel(const QVariant &model) { Q_D(QQmlDelegateModel); @@ -375,7 +432,10 @@ void QQmlDelegateModel::setModel(const QVariant &model) if (d->m_complete) _q_itemsRemoved(0, d->m_count); + d->disconnectFromAbstractItemModel(); d->m_adaptorModel.setModel(model, this, d->m_context->engine()); + d->connectToAbstractItemModel(); + d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles); for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { d->m_adaptorModel.replaceWatchedRoles( @@ -383,7 +443,7 @@ void QQmlDelegateModel::setModel(const QVariant &model) } if (d->m_complete) { - _q_itemsInserted(0, d->m_adaptorModel.count()); + _q_itemsInserted(0, d->adaptorModelCount()); d->requestMoreIfNecessary(); } } @@ -409,7 +469,7 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) return; } bool wasValid = d->m_delegate != nullptr; - d->m_delegate = delegate; + d->m_delegate.setObject(delegate, this); d->m_delegateValidated = false; if (wasValid && d->m_complete) { for (int i = 1; i < d->m_groupCount; ++i) { @@ -470,12 +530,16 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root) if (changed || !d->m_adaptorModel.isValid()) { const int oldCount = d->m_count; d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. + if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) { + // The previous root index was invalidated, so we need to reconnect the model. + d->disconnectFromAbstractItemModel(); d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); + d->connectToAbstractItemModel(); + } if (d->m_adaptorModel.canFetchMore()) d->m_adaptorModel.fetchMore(); if (d->m_complete) { - const int newCount = d->m_adaptorModel.count(); + const int newCount = d->adaptorModelCount(); if (oldCount) _q_itemsRemoved(0, oldCount); if (newCount) @@ -536,25 +600,24 @@ int QQmlDelegateModel::count() const QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) { - QQmlDelegateModel::ReleaseFlags stat = nullptr; if (!object) - return stat; - - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) { - if (cacheItem->releaseObject()) { - cacheItem->destroyObject(); - emitDestroyingItem(object); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - } - cacheItem->Dispose(); - stat |= QQmlInstanceModel::Destroyed; - } else { - stat |= QQmlDelegateModel::Referenced; - } + return QQmlDelegateModel::ReleaseFlags(0); + + QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object); + if (!cacheItem) + return QQmlDelegateModel::ReleaseFlags(0); + + if (!cacheItem->releaseObject()) + return QQmlDelegateModel::Referenced; + + cacheItem->destroyObject(); + emitDestroyingItem(object); + if (cacheItem->incubationTask) { + releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = nullptr; } - return stat; + cacheItem->Dispose(); + return QQmlInstanceModel::Destroyed; } /* @@ -808,6 +871,12 @@ QObject *QQmlDelegateModel::parts() return d->m_parts; } +const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr; +} + void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) { for (int i = 1; i < m_groupCount; ++i) @@ -862,6 +931,13 @@ void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTa } } +void QQmlDelegateModelPrivate::addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it) +{ + m_cache.insert(it.cacheIndex, item); + m_compositor.setFlags(it, 1, Compositor::CacheFlag); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); +} + void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) { int cidx = m_cache.lastIndexOf(cacheItem); @@ -951,10 +1027,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ return nullptr; cacheItem->groups = it->flags; - - m_cache.insert(it.cacheIndex, cacheItem); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + addCacheItem(cacheItem, it); } // Bump the reference counts temporarily so neither the content data or the delegate object @@ -1046,7 +1119,10 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) if (!it->inCache()) return QQmlIncubator::Null; - return d->m_cache.at(it.cacheIndex)->incubationTask->status(); + if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask) + return incubationTask->status(); + + return QQmlIncubator::Ready; } QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) @@ -1266,8 +1342,12 @@ void QQmlDelegateModel::_q_itemsInserted(int index, int count) const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); - if (item->modelIndex() >= index) - item->setModelIndex(item->modelIndex() + count); + if (item->modelIndex() >= index) { + const int newIndex = item->modelIndex() + count; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } } QVector<Compositor::Insert> inserts; @@ -1408,10 +1488,14 @@ void QQmlDelegateModel::_q_itemsRemoved(int index, int count) if (!d->m_cache.contains(item)) continue; - if (item->modelIndex() >= index + count) - item->setModelIndex(item->modelIndex() - count); - else if (item->modelIndex() >= index) - item->setModelIndex(-1); + if (item->modelIndex() >= index + count) { + const int newIndex = item->modelIndex() - count; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } else if (item->modelIndex() >= index) { + item->setModelIndex(-1, -1, -1); + } } QVector<Compositor::Remove> removes; @@ -1456,10 +1540,17 @@ void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); - if (item->modelIndex() >= from && item->modelIndex() < from + count) - item->setModelIndex(item->modelIndex() - from + to); - else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) - item->setModelIndex(item->modelIndex() + difference); + if (item->modelIndex() >= from && item->modelIndex() < from + count) { + const int newIndex = item->modelIndex() - from + to; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) { + const int newIndex = item->modelIndex() + difference; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } } QVector<Compositor::Remove> removes; @@ -1510,13 +1601,13 @@ void QQmlDelegateModel::_q_modelReset() d->m_adaptorModel.rootIndex = QModelIndex(); if (d->m_complete) { - d->m_count = d->m_adaptorModel.count(); + d->m_count = d->adaptorModelCount(); const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); if (item->modelIndex() != -1) - item->setModelIndex(-1); + item->setModelIndex(-1, -1, -1); } QVector<Compositor::Remove> removes; @@ -1552,7 +1643,8 @@ void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int b if (index.parent() == parent && index.row() >= begin && index.row() <= end) { const int oldCount = d->m_count; d->m_count = 0; - d->m_adaptorModel.invalidateModel(this); + d->disconnectFromAbstractItemModel(); + d->m_adaptorModel.invalidateModel(); if (d->m_complete && oldCount > 0) { QVector<Compositor::Remove> removes; @@ -1804,7 +1896,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const QV4::ScopedValue v(scope); uint arrayLength = array->getLength(); for (uint i = 0; i < arrayLength; ++i) { - v = array->getIndexed(i); + v = array->get(i); const QString groupName = v->toQString(); int index = groupNames.indexOf(groupName); if (index != -1) @@ -1906,8 +1998,7 @@ void QV4::Heap::QQmlDelegateModelItemObject::destroy() } -QQmlDelegateModelItem::QQmlDelegateModelItem( - QQmlDelegateModelItemMetaType *metaType, int modelIndex) +QQmlDelegateModelItem::QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column) : v4(metaType->v4Engine) , metaType(metaType) , contextData(nullptr) @@ -1918,6 +2009,8 @@ QQmlDelegateModelItem::QQmlDelegateModelItem( , scriptRef(0) , groups(0) , index(modelIndex) + , row(row) + , column(column) { metaType->addref(); } @@ -1952,6 +2045,26 @@ void QQmlDelegateModelItem::Dispose() delete this; } +void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn) +{ + if (idx == index) + return; + + const int prevRow = row; + const int prevColumn = column; + + index = idx; + row = newRow; + column = newColumn; + + Q_EMIT modelIndexChanged(); + + if (row != prevRow) + emit rowChanged(); + if (column != prevColumn) + emit columnChanged(); +} + void QQmlDelegateModelItem::destroyObject() { Q_ASSERT(object); @@ -2080,22 +2193,29 @@ QQmlDelegateModelAttached::QQmlDelegateModelAttached( , m_previousGroups(cacheItem->groups) { QQml_setParent_noEvent(this, parent); + resetCurrentIndex(); + // Let m_previousIndex be equal to m_currentIndex + std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex)); + + if (!cacheItem->metaType->metaObject) + cacheItem->metaType->initializeMetaObject(); + + QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; + cacheItem->metaType->metaObject->addref(); +} + +void QQmlDelegateModelAttached::resetCurrentIndex() +{ if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { for (int i = 1; i < qMin<int>(m_cacheItem->metaType->groupCount, Compositor::MaximumGroupCount); ++i) - m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; + m_currentIndex[i] = incubationTask->index[i]; } else { QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); Compositor::iterator it = model->m_compositor.find( Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = it.index[i]; + m_currentIndex[i] = it.index[i]; } - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); } /*! @@ -2483,9 +2603,9 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) model->m_cacheMetaType->initializePrototype(); QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine; QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(cacheItem)); + QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem)); QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); - o->setPrototype(p); + o->setPrototypeOf(p); ++cacheItem->scriptRef; return QQmlV4Handle(o); @@ -3194,7 +3314,10 @@ QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index) if (!it->inCache()) return QQmlIncubator::Null; - return model->m_cache.at(it.cacheIndex)->incubationTask->status(); + if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask) + return incubationTask->status(); + + return QQmlIncubator::Ready; } int QQmlPartsModel::indexOf(QObject *item, QObject *) const @@ -3214,7 +3337,10 @@ void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) void QQmlPartsModel::initPackage(int index, QQuickPackage *package) { - emit initItem(index, package->part(m_part)); + if (m_modelUpdatePending) + m_pendingPackageInitializations << index; + else + emit initItem(index, package->part(m_part)); } void QQmlPartsModel::destroyingPackage(QQuickPackage *package) @@ -3226,9 +3352,22 @@ void QQmlPartsModel::destroyingPackage(QQuickPackage *package) void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) { + m_modelUpdatePending = false; emit modelUpdated(changeSet, reset); if (changeSet.difference() != 0) emit countChanged(); + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + QVector<int> pendingPackageInitializations; + qSwap(pendingPackageInitializations, m_pendingPackageInitializations); + for (int index : pendingPackageInitializations) { + if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) + continue; + QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous); + if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) + emit initItem(index, package->part(m_part)); + model->release(object); + } } //============================================================================ @@ -3238,7 +3377,7 @@ struct QQmlDelegateModelGroupChange : QV4::Object V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object) static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) { - return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>(); + return e->memoryManager->allocate<QQmlDelegateModelGroupChange>(); } static QV4::ReturnedValue method_get_index(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { @@ -3275,49 +3414,49 @@ struct QQmlDelegateModelGroupChangeArray : public QV4::Object public: static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes) { - return engine->memoryManager->allocObject<QQmlDelegateModelGroupChangeArray>(changes); + return engine->memoryManager->allocate<QQmlDelegateModelGroupChangeArray>(changes); } quint32 count() const { return d()->changes->count(); } const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); } - static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty) + static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty) { - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); - - if (index >= array->count()) { - if (hasProperty) - *hasProperty = false; - return QV4::Primitive::undefinedValue().asReturnedValue(); - } + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); + QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); + QV4::Scope scope(v4); + QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); + + if (index >= array->count()) { + if (hasProperty) + *hasProperty = false; + return QV4::Primitive::undefinedValue().asReturnedValue(); + } - const QQmlChangeSet::Change &change = array->at(index); + const QQmlChangeSet::Change &change = array->at(index); - QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); - QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); - object->setPrototype(changeProto); - object->d()->change = change; + QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); + QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); + object->setPrototypeOf(changeProto); + object->d()->change = change; - if (hasProperty) - *hasProperty = true; - return object.asReturnedValue(); - } + if (hasProperty) + *hasProperty = true; + return object.asReturnedValue(); + } - static QV4::ReturnedValue get(const QV4::Managed *m, QV4::String *name, bool *hasProperty) - { Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m); - if (name->equals(array->engine()->id_length())) { + if (id == array->engine()->id_length()->propertyKey()) { if (hasProperty) *hasProperty = true; return QV4::Encode(array->count()); } - return Object::get(m, name, hasProperty); + return Object::virtualGet(m, id, receiver, hasProperty); } }; diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index b0786cd088..5c66a90798 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -130,6 +130,8 @@ public: QQmlListProperty<QQmlDelegateModelGroup> groups(); QObject *parts(); + const QAbstractItemModel *abstractItemModel() const override; + bool event(QEvent *) override; static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); @@ -212,6 +214,7 @@ public: QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); ~QQmlDelegateModelAttached() {} + void resetCurrentIndex(); void setCacheItem(QQmlDelegateModelItem *item); QQmlDelegateModel *model() const; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 18980cfd7c..e87f8d4440 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -68,7 +68,7 @@ typedef QQmlListCompositor Compositor; class QQmlDelegateModelAttachedMetaObject; -class QQmlDelegateModelItemMetaType : public QQmlRefCount +class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount { public: QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); @@ -95,9 +95,11 @@ class QQmlDelegateModelItem : public QObject { Q_OBJECT Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) + Q_PROPERTY(int row MEMBER row NOTIFY rowChanged) + Q_PROPERTY(int column MEMBER column NOTIFY columnChanged) Q_PROPERTY(QObject *model READ modelObject CONSTANT) public: - QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex); + QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column); ~QQmlDelegateModelItem(); void referenceObject() { ++objectRef; } @@ -121,7 +123,7 @@ public: int groupIndex(Compositor::Group group); int modelIndex() const { return index; } - void setModelIndex(int idx) { index = idx; Q_EMIT modelIndexChanged(); } + virtual void setModelIndex(int idx, int newRow, int newColumn); virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); } @@ -148,9 +150,13 @@ public: Q_SIGNALS: void modelIndexChanged(); + void rowChanged(); + void columnChanged(); protected: void objectDestroyed(QObject *); + int row; + int column; }; namespace QV4 { @@ -190,8 +196,8 @@ public: void statusChanged(Status) override; void setInitialState(QObject *) override; - QQmlDelegateModelItem *incubating; - QQmlDelegateModelPrivate *vdm; + QQmlDelegateModelItem *incubating = nullptr; + QQmlDelegateModelPrivate *vdm = nullptr; int index[QQmlListCompositor::MaximumGroupCount]; }; @@ -256,6 +262,8 @@ public: void init(); void connectModel(QQmlAdaptorModel *model); + void connectToAbstractItemModel(); + void disconnectFromAbstractItemModel(); void requestMoreIfNecessary(); QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode); @@ -269,6 +277,7 @@ public: Q_EMIT q_func()->initItem(incubationTask->index[m_compositorGroup], item); } void emitDestroyingPackage(QQuickPackage *package); void emitDestroyingItem(QObject *item) { Q_EMIT q_func()->destroyingItem(item); } + void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it); void removeCacheItem(QQmlDelegateModelItem *cacheItem); void updateFilterGroup(); @@ -295,6 +304,8 @@ public: bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups); + int adaptorModelCount() const; + static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group); static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property); static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index); @@ -305,7 +316,7 @@ public: QQmlAdaptorModel m_adaptorModel; QQmlListCompositor m_compositor; - QQmlComponent *m_delegate; + QQmlStrongJSQObjectReference<QQmlComponent> m_delegate; QQmlDelegateModelItemMetaType *m_cacheMetaType; QPointer<QQmlContext> m_context; QQmlDelegateModelParts *m_parts; @@ -378,8 +389,10 @@ private: QString m_part; QString m_filterGroup; QList<QByteArray> m_watchedRoles; + QVector<int> m_pendingPackageInitializations; // vector holds model indices Compositor::Group m_compositorGroup; bool m_inheritGroup; + bool m_modelUpdatePending = true; }; class QMetaPropertyBuilder; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 830c2bef5a..1b64a38bd6 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -62,6 +62,8 @@ #include <QtCore/qdatetime.h> #include <QScopedValueRollback> +Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*); + QT_BEGIN_NAMESPACE // Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. @@ -124,8 +126,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) { - const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; + const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; + const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; Role *r = new Role; r->name = key; @@ -227,6 +229,10 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV data.value<QJSValue>().isCallable()) { type = Role::Function; break; + } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>() + && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) { + type = Role::String; + break; } else { type = Role::List; break; @@ -261,6 +267,75 @@ const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const return r; } +StringOrTranslation::StringOrTranslation(const QString &s) +{ + d.setFlag(); + setString(s); +} + +StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding) +{ + d.setFlag(); + clear(); + d = binding; +} + +StringOrTranslation::~StringOrTranslation() +{ + clear(); +} + +void StringOrTranslation::setString(const QString &s) +{ + d.setFlag(); + clear(); + QStringData *stringData = const_cast<QString &>(s).data_ptr(); + d = stringData; + if (stringData) + stringData->ref.ref(); +} + +void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding) +{ + d.setFlag(); + clear(); + d = binding; +} + +QString StringOrTranslation::toString(const QQmlListModel *owner) const +{ + if (d.isNull()) + return QString(); + if (d.isT1()) { + QStringDataPtr holder = { d.asT1() }; + holder.ptr->ref.ref(); + return QString(holder); + } + if (!owner) + return QString(); + return d.asT2()->valueAsString(owner->m_compilationUnit.data()); +} + +QString StringOrTranslation::asString() const +{ + if (d.isNull()) + return QString(); + if (!d.isT1()) + return QString(); + QStringDataPtr holder = { d.asT1() }; + holder.ptr->ref.ref(); + return QString(holder); +} + +void StringOrTranslation::clear() +{ + if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) { + if (!strData->ref.deref()) + QStringData::deallocate(strData); + } + d = static_cast<QStringData *>(nullptr); +} + QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) { ListElement *e = elements[elementIndex]; @@ -512,7 +587,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } @@ -593,7 +668,7 @@ void ListModel::set(int elementIndex, QV4::Object *object) int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } @@ -717,11 +792,11 @@ ModelNodeMetaObject *ListElement::objectCache() return ModelNodeMetaObject::get(m_objectCache); } -QString *ListElement::getStringProperty(const ListLayout::Role &role) +StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role) { char *mem = getPropertyMemory(role); - QString *s = reinterpret_cast<QString *>(mem); - return s->data_ptr() ? s : nullptr; + StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); + return s; } QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) @@ -806,9 +881,9 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo break; case ListLayout::Role::String: { - QString *value = reinterpret_cast<QString *>(mem); - if (value->data_ptr() != nullptr) - data = *value; + StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem); + if (value->isSet()) + data = value->toString(owner); } break; case ListLayout::Role::Bool: @@ -878,15 +953,13 @@ int ListElement::setStringProperty(const ListLayout::Role &role, const QString & if (role.type == ListLayout::Role::String) { char *mem = getPropertyMemory(role); - QString *c = reinterpret_cast<QString *>(mem); + StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem); bool changed; - if (c->data_ptr() == nullptr) { - new (mem) QString(s); + if (!c->isSet() || c->isTranslation()) changed = true; - } else { - changed = c->compare(s) != 0; - *c = s; - } + else + changed = c->asString().compare(s) != 0; + c->setString(s); if (changed) roleIndex = role.index; } @@ -1048,11 +1121,25 @@ int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValu return roleIndex; } +int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::String) { + char *mem = getPropertyMemory(role); + StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); + s->setTranslation(b); + roleIndex = role.index; + } + + return roleIndex; +} + void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) { char *mem = getPropertyMemory(role); - new (mem) QString(s); + new (mem) StringOrTranslation(s); } void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) @@ -1219,9 +1306,9 @@ void ListElement::destroy(ListLayout *layout) switch (r.type) { case ListLayout::Role::String: { - QString *string = getStringProperty(r); + StringOrTranslation *string = getStringProperty(r); if (string) - string->~QString(); + string->~StringOrTranslation(); } break; case ListLayout::Role::List: @@ -1284,7 +1371,10 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant roleIndex = setDoubleProperty(role, d.toDouble()); break; case ListLayout::Role::String: - roleIndex = setStringProperty(role, d.toString()); + if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>()) + roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>()); + else + roleIndex = setStringProperty(role, d.toString()); break; case ListLayout::Role::Bool: roleIndex = setBoolProperty(role, d.toBool()); @@ -1332,7 +1422,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d ListModel *subModel = new ListModel(role.subLayout, nullptr); int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } roleIndex = setListProperty(role, subModel); @@ -1446,7 +1536,7 @@ void ModelNodeMetaObject::propertyWritten(int index) return; QString propName = QString::fromUtf8(name(index)); - QVariant value = operator[](index); + const QVariant value = this->value(index); QV4::Scope scope(m_model->engine()); QV4::ScopedValue v(scope, scope.engine->fromVariant(value)); @@ -1474,29 +1564,37 @@ void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCo namespace QV4 { -bool ModelObject::put(Managed *m, String *name, const Value &value) +bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isString()) + return Object::virtualPut(m, id, value, receiver); + QString propName = id.toQString(); + ModelObject *that = static_cast<ModelObject*>(m); ExecutionEngine *eng = that->engine(); const int elementIndex = that->d()->elementIndex(); - const QString propName = name->toQString(); int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); if (roleIndex != -1) that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); if (mo->initialized()) - mo->emitPropertyNotification(name->toQString().toUtf8()); + mo->emitPropertyNotification(propName.toUtf8()); return true; } -ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + if (!id.isString()) + return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); + const ModelObject *that = static_cast<const ModelObject*>(m); + Scope scope(that); + ScopedString name(scope, id.asStringOrSymbol()); const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); if (!role) - return QObjectWrapper::get(m, name, hasProperty); + return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); if (hasProperty) *hasProperty = true; @@ -1512,7 +1610,7 @@ ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty return that->engine()->fromVariant(value); } -void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +void ModelObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { ModelObject *that = static_cast<ModelObject*>(m); ExecutionEngine *v4 = that->engine(); @@ -1532,7 +1630,7 @@ void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, u // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add // unnecessary entries that relate to the roles used. These just create extra work // later on as they will just be ignored. - QV4::Object::advanceIterator(m, it, name, index, p, attributes); + QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); } DEFINE_OBJECT_VTABLE(ModelObject); @@ -1809,6 +1907,7 @@ QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::E m_listModel = data; m_engine = engine; + m_compilationUnit = owner->m_compilationUnit; } QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) @@ -1828,6 +1927,7 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen ListModel::sync(orig->m_listModel, m_listModel); m_engine = nullptr; + m_compilationUnit = orig->m_compilationUnit; } QQmlListModel::~QQmlListModel() @@ -2244,7 +2344,7 @@ void QQmlListModel::insert(QQmlV4Function *args) int objectArrayLength = objectArray->getLength(); emitItemsAboutToBeInserted(index, objectArrayLength); for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->getIndexed(i); + argObject = objectArray->get(i); if (m_dynamicRoles) { m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); @@ -2356,7 +2456,7 @@ void QQmlListModel::append(QQmlV4Function *args) emitItemsAboutToBeInserted(index, objectArrayLength); for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->getIndexed(i); + argObject = objectArray->get(i); if (m_dynamicRoles) { m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); @@ -2434,7 +2534,7 @@ QQmlV4Handle QQmlListModel::get(int index) const QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); QQmlData *ddata = QQmlData::get(object); if (ddata->jsWrapper.isNullOrUndefined()) { - result = scope.engine->memoryManager->allocObject<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); + result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); // Keep track of the QObjectWrapper in persistent value storage ddata->jsWrapper.set(scope.engine, result); } else { @@ -2551,12 +2651,12 @@ void QQmlListModel::sync() qmlWarning(this) << "List sync() can only be called from a WorkerScript"; } -bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding) +bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) { if (binding->type >= QV4::CompiledData::Binding::Type_Object) { const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = qmlUnit->objectAt(targetObjectIndex); - QString objName = qmlUnit->stringAt(target->inheritedTypeNameIndex); + const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); + QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex); if (objName != listElementTypeName) { const QMetaObject *mo = resolveType(objName); if (mo != &QQmlListElement::staticMetaObject) { @@ -2566,23 +2666,23 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, listElementTypeName = objName; // cache right name for next time } - if (!qmlUnit->stringAt(target->idNameIndex).isEmpty()) { + if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) { error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); return false; } const QV4::CompiledData::Binding *binding = target->bindingTable(); for (quint32 i = 0; i < target->nBindings; ++i, ++binding) { - QString propName = qmlUnit->stringAt(binding->propertyNameIndex); + QString propName = compilationUnit->stringAt(binding->propertyNameIndex); if (propName.isEmpty()) { error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements")); return false; } - if (!verifyProperty(qmlUnit, binding)) + if (!verifyProperty(compilationUnit, binding)) return false; } } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(qmlUnit); + QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) { QByteArray script = scriptStr.toUtf8(); bool ok; @@ -2597,14 +2697,14 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, return true; } -bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) +bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) { - const QString elementName = qmlUnit->stringAt(binding->propertyNameIndex); + const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex); bool roleSet = false; if (binding->type >= QV4::CompiledData::Binding::Type_Object) { const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = qmlUnit->objectAt(targetObjectIndex); + const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); ListModel *subModel = nullptr; if (outterElementIndex == -1) { @@ -2625,20 +2725,22 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp const QV4::CompiledData::Binding *subBinding = target->bindingTable(); for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) { - roleSet |= applyProperty(compilationUnit, qmlUnit, subBinding, subModel, elementIndex); + roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex); } } else { QVariant value; - if (binding->evaluatesToString()) { - value = binding->valueAsString(qmlUnit); + if (binding->isTranslationBinding()) { + value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding); + } else if (binding->evaluatesToString()) { + value = binding->valueAsString(compilationUnit.data()); } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - value = binding->valueAsNumber(); + value = binding->valueAsNumber(compilationUnit->constants); } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { value = binding->valueAsBoolean(); } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(qmlUnit); + QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); if (definesEmptyList(scriptStr)) { const ListLayout::Role &role = model->getOrCreateListRole(elementName); ListModel *emptyModel = new ListModel(role.subLayout, nullptr); @@ -2673,35 +2775,34 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp return roleSet; } -void QQmlListModelParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { listElementTypeName = QString(); // unknown for (const QV4::CompiledData::Binding *binding : bindings) { - QString propName = qmlUnit->stringAt(binding->propertyNameIndex); + QString propName = compilationUnit->stringAt(binding->propertyNameIndex); if (!propName.isEmpty()) { // isn't default property error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName)); return; } - if (!verifyProperty(qmlUnit, binding)) + if (!verifyProperty(compilationUnit, binding)) return; } } -void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlListModel *rv = static_cast<QQmlListModel *>(obj); rv->m_engine = qmlEngine(rv)->handle(); - - const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data; + rv->m_compilationUnit = compilationUnit; bool setRoles = false; for (const QV4::CompiledData::Binding *binding : bindings) { if (binding->type != QV4::CompiledData::Binding::Type_Object) continue; - setRoles |= applyProperty(compilationUnit, qmlUnit, binding, rv->m_listModel, /*outter element index*/-1); + setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1); } if (setRoles == false) diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 0c0859dc80..95b797c898 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -64,6 +64,8 @@ #include <private/qv4engine_p.h> #include <private/qpodvector_p.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE @@ -122,6 +124,7 @@ private: friend class ListElement; friend class DynamicRoleModelNode; friend class DynamicRoleModelNodeMetaObject; + friend struct StringOrTranslation; // Constructs a flat list model for a worker agent QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); @@ -133,6 +136,7 @@ private: QQmlListModelWorkerAgent *m_agent; mutable QV4::ExecutionEngine *m_engine; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; bool m_mainThread; bool m_primary; @@ -183,13 +187,13 @@ public: QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; - void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; + void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; + void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; private: - bool verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding); + bool verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding); // returns true if a role was set - bool applyProperty(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex); + bool applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex); static bool definesEmptyList(const QString &); diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index ad5e94c909..e4a850e8a5 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -57,6 +57,8 @@ #include <private/qv4qobjectwrapper_p.h> #include <qqml.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE @@ -142,16 +144,6 @@ protected: private: using QQmlOpenMetaObject::setValue; - void setValue(const QByteArray &name, const QVariant &val, bool force) - { - if (force) { - QVariant existingValue = value(name); - if (existingValue.isValid()) { - (*this)[name] = QVariant(); - } - } - setValue(name, val); - } void emitDirectNotifies(const int *changedRoles, int roleCount); @@ -181,12 +173,13 @@ struct ModelObject : public QObjectWrapper { struct ModelObject : public QObjectWrapper { - static bool put(Managed *m, String *name, const Value& value); - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - V4_OBJECT2(ModelObject, QObjectWrapper) V4_NEEDS_DESTROY + +protected: + static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; } // namespace QV4 @@ -252,6 +245,22 @@ private: QStringHash<Role *> roleHash; }; +struct StringOrTranslation +{ + explicit StringOrTranslation(const QString &s); + explicit StringOrTranslation(const QV4::CompiledData::Binding *binding); + ~StringOrTranslation(); + bool isSet() const { return d.flag(); } + bool isTranslation() const { return d.isT2(); } + void setString(const QString &s); + void setTranslation(const QV4::CompiledData::Binding *binding); + QString toString(const QQmlListModel *owner) const; + QString asString() const; +private: + void clear(); + QBiPointer<QStringData, const QV4::CompiledData::Binding> d; +}; + /*! \internal */ @@ -287,6 +296,7 @@ private: int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f); + int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b); void setStringPropertyFast(const ListLayout::Role &role, const QString &s); void setDoublePropertyFast(const ListLayout::Role &role, double n); @@ -301,7 +311,7 @@ private: QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng); ListModel *getListProperty(const ListLayout::Role &role); - QString *getStringProperty(const ListLayout::Role &role); + StringOrTranslation *getStringProperty(const ListLayout::Role &role); QObject *getQObjectProperty(const ListLayout::Role &role); QPointer<QObject> *getGuardProperty(const ListLayout::Role &role); QVariantMap *getVariantMapProperty(const ListLayout::Role &role); diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h index 2120f25744..ae2d4b11e0 100644 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ b/src/qml/types/qqmllistmodelworkeragent_p.h @@ -59,6 +59,8 @@ #include <private/qv8engine_p.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index e217b63c6f..9c170cb008 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -39,7 +39,9 @@ #include "qqmlmodelsmodule_p.h" #include <QtCore/qitemselectionmodel.h> +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> +#endif #if QT_CONFIG(qml_delegate_model) #include <private/qqmldelegatemodel_p.h> #endif @@ -51,8 +53,10 @@ void QQmlModelsModule::defineModule() { const char uri[] = "QtQml.Models"; +#if QT_CONFIG(qml_list_model) qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement"); qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser); +#endif #if QT_CONFIG(qml_delegate_model) qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h index 267828dcdd..4ac4f1c65b 100644 --- a/src/qml/types/qqmlobjectmodel_p.h +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE class QObject; class QQmlChangeSet; +class QAbstractItemModel; class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject { @@ -83,6 +84,7 @@ public: virtual QQmlIncubator::Status incubationStatus(int index) = 0; virtual int indexOf(QObject *object, QObject *objectContext) const = 0; + virtual const QAbstractItemModel *abstractItemModel() const { return nullptr; } Q_SIGNALS: void countChanged(); diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp new file mode 100644 index 0000000000..dcad84cfb8 --- /dev/null +++ b/src/qml/types/qqmltableinstancemodel.cpp @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltableinstancemodel_p.h" +#include "qqmltableinstancemodel_p.h" + +#include <QtCore/QTimer> + +#include <QtQml/private/qqmlincubator_p.h> +#include <QtQml/private/qqmlchangeset_p.h> +#include <QtQml/private/qqmlcomponent_p.h> + +QT_BEGIN_NAMESPACE + +const char* kModelItemTag = "_tableinstancemodel_modelItem"; + +bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem) +{ + if (!modelItem->incubationTask) + return true; + + const auto status = modelItem->incubationTask->status(); + return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error); +} + +void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem) +{ + Q_ASSERT(modelItem); + + delete modelItem->object; + modelItem->object = nullptr; + + if (modelItem->contextData) { + modelItem->contextData->invalidate(); + Q_ASSERT(modelItem->contextData->refCount == 1); + modelItem->contextData = nullptr; + } + + modelItem->deleteLater(); +} + +QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent) + : QQmlInstanceModel(*(new QObjectPrivate()), parent) + , m_qmlContext(qmlContext) + , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList())) +{ +} + +QQmlTableInstanceModel::~QQmlTableInstanceModel() +{ + deleteAllFinishedIncubationTasks(); + + // If we have model items loaded at this point, it means that + // the view is still holding references to them. So basically + // the view needs to be deleted first (and thereby release all + // its items), before this destructor is run. + Q_ASSERT(m_modelItems.isEmpty()); +} + +QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode) +{ + Q_ASSERT(m_delegate); + Q_ASSERT(index >= 0 && index < m_adaptorModel.count()); + Q_ASSERT(m_qmlContext && m_qmlContext->isValid()); + + // Check if we've already created an item for the given index + QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr); + + if (!modelItem) { + // Create a new model item + modelItem = m_adaptorModel.createItem(m_metaType, index); + if (!modelItem) { + qWarning() << Q_FUNC_INFO << "Adaptor model failed creating a model item"; + return nullptr; + } + m_modelItems.insert(index, modelItem); + } + + if (modelItem->object) { + // The model item has already been incubated. So + // just bump the ref-count and return it. + modelItem->referenceObject(); + return modelItem->object; + } + + // Guard the model item temporarily so that it's not deleted from + // incubatorStatusChanged(), in case the incubation is done synchronously. + modelItem->scriptRef++; + + if (modelItem->incubationTask) { + // We're already incubating the model item from a previous request. If the previous call requested + // the item async, but the current request needs it sync, we need to force-complete the incubation. + const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); + if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) + modelItem->incubationTask->forceCompletion(); + } else { + modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); + + QQmlContextData *ctxt = new QQmlContextData; + QQmlContext *creationContext = m_delegate->creationContext(); + ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); + ctxt->contextObject = modelItem; + modelItem->contextData = ctxt; + + QQmlComponentPrivate::get(m_delegate)->incubateObject( + modelItem->incubationTask, + m_delegate, + m_qmlContext->engine(), + ctxt, + QQmlContextData::get(m_qmlContext)); + } + + // Remove the temporary guard + modelItem->scriptRef--; + Q_ASSERT(modelItem->scriptRef == 0); + + if (!isDoneIncubating(modelItem)) + return nullptr; + + // When incubation is done, the task should be removed + Q_ASSERT(!modelItem->incubationTask); + + if (!modelItem->object) { + // The object was incubated synchronously (otherwise we would return above). But since + // we have no object, the incubation must have failed. And when we have no object, there + // should be no object references either. And there should also not be any internal script + // refs at this point. So we delete the model item. + Q_ASSERT(!modelItem->isObjectReferenced()); + Q_ASSERT(!modelItem->isReferenced()); + m_modelItems.remove(modelItem->index); + delete modelItem; + return nullptr; + } + + // Incubation was completed sync and successful + modelItem->referenceObject(); + return modelItem->object; +} + +QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object) +{ + Q_ASSERT(object); + auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag)); + Q_ASSERT(modelItem); + + if (!modelItem->releaseObject()) + return QQmlDelegateModel::Referenced; + + if (modelItem->isReferenced()) { + // We still have an internal reference to this object, which means that we are told to release an + // object while the createdItem signal for it is still on the stack. This can happen when objects + // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch + // up with. The view might then find that the object is no longer visible and should be released. + // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers + // points of view, it should consider it destroyed. + return QQmlDelegateModel::Destroyed; + } + + m_modelItems.remove(modelItem->index); + modelItem->destroyObject(); + emit destroyingItem(object); + + delete modelItem; + return QQmlInstanceModel::Destroyed; +} + +void QQmlTableInstanceModel::cancel(int index) +{ + auto modelItem = m_modelItems.value(index); + Q_ASSERT(modelItem); + + // Since the view expects the item to be incubating, there should be + // an incubation task. And since the incubation is not done, no-one + // should yet have received, and therfore hold a reference to, the object. + Q_ASSERT(modelItem->incubationTask); + Q_ASSERT(!modelItem->isObjectReferenced()); + + m_modelItems.remove(index); + + if (modelItem->object) + delete modelItem->object; + + // modelItem->incubationTask will be deleted from the modelItems destructor + delete modelItem; +} + +void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status) +{ + QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate; + Q_ASSERT(modelItem->incubationTask); + + modelItem->incubationTask = nullptr; + incubationTask->modelItemToIncubate = nullptr; + + if (status == QQmlIncubator::Ready) { + // Tag the incubated object with the model item for easy retrieval upon release etc. + modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem)); + + // Emit that the item has been created. What normally happens next is that the view + // upon receiving the signal asks for the model item once more. And since the item is + // now in the map, it will be returned directly. + Q_ASSERT(modelItem->object); + modelItem->scriptRef++; + emit createdItem(modelItem->index, modelItem->object); + modelItem->scriptRef--; + } else if (status == QQmlIncubator::Error) { + qWarning() << "Error incubating delegate:" << incubationTask->errors(); + } + + if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) { + // We have no internal reference to the model item, and the view has no + // reference to the incubated object. So just delete the model item. + // Note that being here means that the object was incubated _async_ + // (otherwise modelItem->isReferenced() would be true). + m_modelItems.remove(modelItem->index); + + if (modelItem->object) { + modelItem->scriptRef++; + emit destroyingItem(modelItem->object); + modelItem->scriptRef--; + Q_ASSERT(!modelItem->isReferenced()); + } + + deleteModelItemLater(modelItem); + } + + deleteIncubationTaskLater(incubationTask); +} + +QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) { + const auto modelItem = m_modelItems.value(index, nullptr); + if (!modelItem) + return QQmlIncubator::Null; + + if (modelItem->incubationTask) + return modelItem->incubationTask->status(); + + // Since we clear the incubation task when we're done + // incubating, it means that the status is Ready. + return QQmlIncubator::Ready; +} + +void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask) +{ + // We often need to post-delete incubation tasks, since we cannot + // delete them while we're in the middle of an incubation change callback. + Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask)); + m_finishedIncubationTasks.append(incubationTask); + if (m_finishedIncubationTasks.count() == 1) + QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks); +} + +void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks() +{ + qDeleteAll(m_finishedIncubationTasks); + m_finishedIncubationTasks.clear(); +} + +QVariant QQmlTableInstanceModel::model() const +{ + return m_adaptorModel.model(); +} + +void QQmlTableInstanceModel::setModel(const QVariant &model) +{ + m_adaptorModel.setModel(model, this, m_qmlContext->engine()); +} + +QQmlComponent *QQmlTableInstanceModel::delegate() const +{ + return m_delegate; +} + +void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate) +{ + m_delegate = delegate; +} + +const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const +{ + return m_adaptorModel.aim(); +} + +// -------------------------------------------------------- + +void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object) +{ + modelItemToIncubate->object = object; + emit tableInstanceModel->initItem(modelItemToIncubate->index, object); +} + +void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status) +{ + if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate)) + return; + + // We require the view to cancel any ongoing load + // requests before the tableInstanceModel is destructed. + Q_ASSERT(tableInstanceModel); + + tableInstanceModel->incubatorStatusChanged(this, status); +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h new file mode 100644 index 0000000000..fb222d8bc4 --- /dev/null +++ b/src/qml/types/qqmltableinstancemodel_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTABLEINSTANCEMODEL_P_H +#define QQMLTABLEINSTANCEMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQml/private/qqmldelegatemodel_p.h> +#include <QtQml/private/qqmldelegatemodel_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlTableInstanceModel; + +class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask +{ +public: + QQmlTableInstanceModelIncubationTask( + QQmlTableInstanceModel *tableInstanceModel + , QQmlDelegateModelItem* modelItemToIncubate + , IncubationMode mode) + : QQDMIncubationTask(nullptr, mode) + , modelItemToIncubate(modelItemToIncubate) + , tableInstanceModel(tableInstanceModel) { + clear(); + } + + void statusChanged(Status status) override; + void setInitialState(QObject *object) override; + + QQmlDelegateModelItem *modelItemToIncubate = nullptr; + QQmlTableInstanceModel *tableInstanceModel = nullptr; +}; + +class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel +{ +public: + QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent = nullptr); + ~QQmlTableInstanceModel() override; + + int count() const override { return m_adaptorModel.count(); } + int rows() const { return m_adaptorModel.rowCount(); } + int columns() const { return m_adaptorModel.columnCount(); } + + bool isValid() const override { return true; } + + QVariant model() const; + void setModel(const QVariant &model); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *); + + const QAbstractItemModel *abstractItemModel() const override; + + QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; + ReleaseFlags release(QObject *) override; + void cancel(int) override; + + QQmlIncubator::Status incubationStatus(int index) override; + + QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); } + void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); } + int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; } + +private: + QQmlComponent *m_delegate = nullptr; + QQmlAdaptorModel m_adaptorModel; + QPointer<QQmlContext> m_qmlContext; + QQmlDelegateModelItemMetaType *m_metaType; + + QHash<int, QQmlDelegateModelItem *> m_modelItems; + QList<QQmlIncubator *> m_finishedIncubationTasks; + + void incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *dmIncubationTask, QQmlIncubator::Status status); + void deleteIncubationTaskLater(QQmlIncubator *incubationTask); + void deleteAllFinishedIncubationTasks(); + + static bool isDoneIncubating(QQmlDelegateModelItem *modelItem); + static void deleteModelItemLater(QQmlDelegateModelItem *modelItem); + + friend class QQmlTableInstanceModelIncubationTask; +}; + +QT_END_NAMESPACE + +#endif // QQMLTABLEINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h index d597869994..0160e97a2f 100644 --- a/src/qml/types/qqmltimer_p.h +++ b/src/qml/types/qqmltimer_p.h @@ -57,6 +57,8 @@ #include <private/qtqmlglobal_p.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class QQmlTimerPrivate; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 98f819337b..f8879160b2 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -37,9 +37,12 @@ ** ****************************************************************************/ +#include "qtqmlglobal_p.h" #include "qquickworkerscript_p.h" +#if QT_CONFIG(qml_list_model) #include "qqmllistmodel_p.h" #include "qqmllistmodelworkeragent_p.h" +#endif #include <private/qqmlengine_p.h> #include <private/qqmlexpression_p.h> @@ -201,7 +204,7 @@ private: }; QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) - : QV8Engine(nullptr, new QV4::ExecutionEngine), p(parent) + : QV8Engine(new QV4::ExecutionEngine), p(parent) #if QT_CONFIG(qml_network) , accessManager(nullptr) #endif @@ -241,14 +244,13 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() QV4::Scope scope(m_v4Engine); QV4::ExecutionContext *globalContext = scope.engine->rootContext(); - onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::ContextType::Global, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro Q_ASSERT(!scope.engine->hasException); - QV4::Script createsendscript(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + QV4::Script createsendscript(globalContext, QV4::Compiler::ContextType::Global, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro QV4::ScopedFunctionObject createsendconstructor(scope, createsendscript.run()); Q_ASSERT(!scope.engine->hasException); QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); - QV4::ScopedValue function(scope, QV4::FunctionObject::createBuiltinFunction(globalContext, name, - QQuickWorkerScriptEnginePrivate::method_sendMessage)); + QV4::ScopedValue function(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1)); QV4::JSCallData jsCallData(scope, 1); jsCallData->args[0] = function; *jsCallData->thisObject = m_v4Engine->global(); diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index 8bcbd6e544..6a74d70a0e 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -1,28 +1,36 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ $$PWD/qqmlconnections.cpp \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlmodelsmodule.cpp \ $$PWD/qqmlmodelindexvaluetype.cpp \ $$PWD/qqmlobjectmodel.cpp \ $$PWD/qquickpackage.cpp \ $$PWD/qquickworkerscript.cpp \ - $$PWD/qqmlinstantiator.cpp + $$PWD/qqmlinstantiator.cpp \ + $$PWD/qqmltableinstancemodel.cpp HEADERS += \ $$PWD/qqmlbind_p.h \ $$PWD/qqmlconnections_p.h \ - $$PWD/qqmllistmodel_p.h \ - $$PWD/qqmllistmodel_p_p.h \ - $$PWD/qqmllistmodelworkeragent_p.h \ $$PWD/qqmlmodelsmodule_p.h \ $$PWD/qqmlmodelindexvaluetype_p.h \ $$PWD/qqmlobjectmodel_p.h \ $$PWD/qquickpackage_p.h \ $$PWD/qquickworkerscript_p.h \ $$PWD/qqmlinstantiator_p.h \ - $$PWD/qqmlinstantiator_p_p.h + $$PWD/qqmlinstantiator_p_p.h \ + $$PWD/qqmltableinstancemodel_p.h + +qtConfig(qml-list-model) { + SOURCES += \ + $$PWD/qqmllistmodel.cpp \ + $$PWD/qqmllistmodelworkeragent.cpp + + HEADERS += \ + $$PWD/qqmllistmodel_p.h \ + $$PWD/qqmllistmodel_p_p.h \ + $$PWD/qqmllistmodelworkeragent_p.h +} qtConfig(qml-delegate-model) { SOURCES += \ @@ -33,7 +41,7 @@ qtConfig(qml-delegate-model) { $$PWD/qqmldelegatemodel_p_p.h } -qtConfig(animation) { +qtConfig(qml-animation) { SOURCES += \ $$PWD/qqmltimer.cpp |