diff --git a/qqmlsortfilterproxymodel.cpp b/qqmlsortfilterproxymodel.cpp index 3ebca88..9bf44c3 100644 --- a/qqmlsortfilterproxymodel.cpp +++ b/qqmlsortfilterproxymodel.cpp @@ -380,10 +380,45 @@ void QQmlSortFilterProxyModel::resetInternalData() void QQmlSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { - if (sourceModel && sourceModel->roleNames().isEmpty()) { // workaround for when a model has no roles and roles are added when the model is populated (ListModel) - // QTBUG-57971 - connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &QQmlSortFilterProxyModel::initRoles); + // QML built-in type ListModel behaves in a specific way regarding roles + // initialization (QTBUG-57971). Empty model has no roles, they become + // available after first insertion. However modelAboutToBeReset/modelReset + // is not emited. It means that roles may change not only in between model + // resets but also on the first insertion. + // In a simple case, where ListModel (or other model behaving in that way) + // is direct source of SFPM, situation is relatively simple - if source + // has no roles, SFPM should try to initialize them on first insertion + // However this behavior has far-reaching consequences, because the + // ListModel-like model may not be a direct source for SFPM. E.g. there may + // by another SFPM in between, with proxy roles defined: + // + // ListModel -> SFPM 1 (with proxy roles) -> SFPM 2 + // + // In such scenario SFPM 2 will always have model with roles as it's source + // (at least proxy roles). It means that on first insertion right after + // SFPM creation or after source model reset it's necessary to re-initialize + // role names if the source was empty before insertion. + + if (auto currentSource = this->sourceModel()) { + disconnect(currentSource, &QAbstractItemModel::rowsInserted, this, + &QQmlSortFilterProxyModel::initRoles); + disconnect(currentSource, &QAbstractItemModel::modelReset, this, nullptr); } + + if (sourceModel && sourceModel->rowCount() == 0) + connect(sourceModel, &QAbstractItemModel::rowsInserted, this, + &QQmlSortFilterProxyModel::initRoles, Qt::UniqueConnection); + + if (sourceModel) { + connect(sourceModel, &QAbstractItemModel::modelReset, this, [sourceModel, this]() { + if (sourceModel->rowCount() != 0) + return; + + connect(sourceModel, &QAbstractItemModel::rowsInserted, this, + &QQmlSortFilterProxyModel::initRoles, Qt::UniqueConnection); + }); + } + QSortFilterProxyModel::setSourceModel(sourceModel); }