diff options
Diffstat (limited to 'src/qmlmodels/qqmltableinstancemodel.cpp')
-rw-r--r-- | src/qmlmodels/qqmltableinstancemodel.cpp | 157 |
1 files changed, 91 insertions, 66 deletions
diff --git a/src/qmlmodels/qqmltableinstancemodel.cpp b/src/qmlmodels/qqmltableinstancemodel.cpp index 5c30a8eb24..dcc15f90a5 100644 --- a/src/qmlmodels/qqmltableinstancemodel.cpp +++ b/src/qmlmodels/qqmltableinstancemodel.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmltableinstancemodel_p.h" #include "qqmlabstractdelegatecomponent_p.h" @@ -65,13 +29,7 @@ void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelIt delete modelItem->object; modelItem->object = nullptr; - - if (modelItem->contextData) { - modelItem->contextData->invalidate(); - Q_ASSERT(modelItem->contextData->refCount() == 1); - modelItem->contextData = nullptr; - } - + modelItem->contextData.reset(); modelItem->deleteLater(); } @@ -103,8 +61,7 @@ QQmlTableInstanceModel::~QQmlTableInstanceModel() if (modelItem->object) { delete modelItem->object; modelItem->object = nullptr; - modelItem->contextData->invalidate(); - modelItem->contextData = nullptr; + modelItem->contextData.reset(); } } @@ -208,6 +165,9 @@ QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, Q_ASSERT(object); auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag)); Q_ASSERT(modelItem); + // Ensure that the object was incubated by this QQmlTableInstanceModel + Q_ASSERT(m_modelItems.contains(modelItem->index)); + Q_ASSERT(m_modelItems[modelItem->index]->object == object); if (!modelItem->releaseObject()) return QQmlDelegateModel::Referenced; @@ -257,6 +217,9 @@ void QQmlTableInstanceModel::dispose(QObject *object) // The item is not referenced by anyone Q_ASSERT(!modelItem->isObjectReferenced()); Q_ASSERT(!modelItem->isReferenced()); + // Ensure that the object was incubated by this QQmlTableInstanceModel + Q_ASSERT(m_modelItems.contains(modelItem->index)); + Q_ASSERT(m_modelItems[modelItem->index]->object == object); m_modelItems.remove(modelItem->index); @@ -331,17 +294,31 @@ void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); QQmlContext *creationContext = modelItem->delegate->creationContext(); - QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted( - QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); - ctxt->setContextObject(modelItem); - modelItem->contextData = ctxt; - - QQmlComponentPrivate::get(modelItem->delegate)->incubateObject( - modelItem->incubationTask, - modelItem->delegate, - m_qmlContext->engine(), - ctxt, - QQmlContextData::get(m_qmlContext)); + const QQmlRefPointer<QQmlContextData> componentContext + = QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()); + + QQmlComponentPrivate *cp = QQmlComponentPrivate::get(modelItem->delegate); + if (cp->isBound()) { + modelItem->contextData = componentContext; + cp->incubateObject( + modelItem->incubationTask, + modelItem->delegate, + m_qmlContext->engine(), + componentContext, + QQmlContextData::get(m_qmlContext)); + } else { + QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted( + QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); + ctxt->setContextObject(modelItem); + modelItem->contextData = ctxt; + + cp->incubateObject( + modelItem->incubationTask, + modelItem->delegate, + m_qmlContext->engine(), + ctxt, + QQmlContextData::get(m_qmlContext)); + } } // Remove the temporary guard @@ -404,13 +381,41 @@ QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) { return QQmlIncubator::Ready; } +bool QQmlTableInstanceModel::setRequiredProperty(int index, const QString &name, const QVariant &value) +{ + // This function can be called from the view upon + // receiving the initItem signal. It can be used to + // give all required delegate properties used by the + // view an initial value. + const auto modelItem = m_modelItems.value(index, nullptr); + if (!modelItem) + return false; + if (!modelItem->object) + return false; + if (!modelItem->incubationTask) + return false; + + bool wasInRequired = false; + const auto task = QQmlIncubatorPrivate::get(modelItem->incubationTask); + RequiredProperties *props = task->requiredProperties(); + if (props->empty()) + return false; + + QQmlProperty componentProp = QQmlComponentPrivate::removePropertyFromRequired( + modelItem->object, name, props, QQmlEnginePrivate::get(task->enginePriv), + &wasInRequired); + if (wasInRequired) + componentProp.write(value); + return wasInRequired; +} + 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) + if (m_finishedIncubationTasks.size() == 1) QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks); } @@ -431,11 +436,15 @@ void QQmlTableInstanceModel::setModel(const QVariant &model) // needs to stay in sync with the model. So we need to drain the pool // completely when the model changes. drainReusableItemsPool(0); - if (auto const aim = abstractItemModel()) + if (auto const aim = abstractItemModel()) { disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); - m_adaptorModel.setModel(model, this); - if (auto const aim = abstractItemModel()) + disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, this, &QQmlTableInstanceModel::modelAboutToBeResetCallback); + } + m_adaptorModel.setModel(model); + if (auto const aim = abstractItemModel()) { connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); + connect(aim, &QAbstractItemModel::modelAboutToBeReset, this, &QQmlTableInstanceModel::modelAboutToBeResetCallback); + } } void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) @@ -453,6 +462,21 @@ void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const } } +void QQmlTableInstanceModel::modelAboutToBeResetCallback() +{ + // When the model is reset, we can no longer rely on any of the data it has + // provided us so far. Normally it's enough for the view to recreate all the + // delegate items in that case, except if the model roles has changed as well + // (since those are cached by QQmlAdaptorModel / Accessors). For the latter case, we + // simply set the model once more in the delegate model to rebuild everything. + auto const aim = abstractItemModel(); + auto oldRoleNames = aim->roleNames(); + QObject::connect(aim, &QAbstractItemModel::modelReset, this, [this, aim, oldRoleNames](){ + if (oldRoleNames != aim->roleNames()) + setModel(model()); + }, Qt::SingleShotConnection); +} + QQmlComponent *QQmlTableInstanceModel::delegate() const { return m_delegate; @@ -484,10 +508,11 @@ const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object) { initializeRequiredProperties(modelItemToIncubate, object); - if (QQmlIncubatorPrivate::get(this)->requiredProperties().empty()) { - modelItemToIncubate->object = object; - emit tableInstanceModel->initItem(modelItemToIncubate->index, object); - } else { + modelItemToIncubate->object = object; + emit tableInstanceModel->initItem(modelItemToIncubate->index, object); + + if (!QQmlIncubatorPrivate::get(this)->requiredProperties()->empty()) { + modelItemToIncubate->object = nullptr; object->deleteLater(); } } |