Hello community, here is the log from the commit of package kitemmodels for openSUSE:Factory checked in at 2020-10-12 13:55:20 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitemmodels (Old) and /work/SRC/openSUSE:Factory/.kitemmodels.new.3486 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitemmodels" Mon Oct 12 13:55:20 2020 rev:84 rq:840830 version:5.75.0 Changes: -------- --- /work/SRC/openSUSE:Factory/kitemmodels/kitemmodels.changes 2020-09-18 14:41:40.368016928 +0200 +++ /work/SRC/openSUSE:Factory/.kitemmodels.new.3486/kitemmodels.changes 2020-10-12 13:57:05.362160427 +0200 @@ -1,0 +2,12 @@ +Mon Oct 5 08:33:20 UTC 2020 - Christophe Giboudeaux <[email protected]> + +- Update to 5.75.0 + * New feature release + * For more details please see: + * https://kde.org/announcements/kde-frameworks-5.75.0 +- Changes since 5.74.0: + * add reference to Qt bug + * ignore sourceDataChanged on invalid indexes + * Support for KDescendantProxyModel "collapsing" nodes + +------------------------------------------------------------------- Old: ---- kitemmodels-5.74.0.tar.xz kitemmodels-5.74.0.tar.xz.sig New: ---- kitemmodels-5.75.0.tar.xz kitemmodels-5.75.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitemmodels.spec ++++++ --- /var/tmp/diff_new_pack.qUBo9I/_old 2020-10-12 13:57:07.722161412 +0200 +++ /var/tmp/diff_new_pack.qUBo9I/_new 2020-10-12 13:57:07.726161413 +0200 @@ -17,7 +17,7 @@ %define lname libKF5ItemModels5 -%define _tar_path 5.74 +%define _tar_path 5.75 # Full KF5 version (e.g. 5.33.0) %{!?_kf5_version: %global _kf5_version %{version}} # Last major and minor KF5 version (e.g. 5.33) @@ -26,7 +26,7 @@ # Only needed for the package signature condition %bcond_without lang Name: kitemmodels -Version: 5.74.0 +Version: 5.75.0 Release: 0 Summary: Set of item models extending the Qt model-view framework License: LGPL-2.1-or-later ++++++ kitemmodels-5.74.0.tar.xz -> kitemmodels-5.75.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/CMakeLists.txt new/kitemmodels-5.75.0/CMakeLists.txt --- old/kitemmodels-5.74.0/CMakeLists.txt 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/CMakeLists.txt 2020-10-04 11:53:39.000000000 +0200 @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.5) -set(KF5_VERSION "5.74.0") # handled by release scripts +set(KF5_VERSION "5.75.0") # handled by release scripts project(KItemModels VERSION ${KF5_VERSION}) include(FeatureSummary) -find_package(ECM 5.74.0 NO_MODULE) +find_package(ECM 5.75.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/autotests/kdescendantsproxymodeltest.cpp new/kitemmodels-5.75.0/autotests/kdescendantsproxymodeltest.cpp --- old/kitemmodels-5.74.0/autotests/kdescendantsproxymodeltest.cpp 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/autotests/kdescendantsproxymodeltest.cpp 2020-10-04 11:53:39.000000000 +0200 @@ -7,14 +7,235 @@ #include "kdescendantsproxymodel.h" #include <QStandardItemModel> +#include <QAbstractListModel> #include <QTest> #include <QSignalSpy> +struct Node +{ + QString label; + Node *parent = nullptr; + QList<Node *> children; +}; + +class SimpleObjectModel: public QAbstractListModel +{ + Q_OBJECT +public: + explicit SimpleObjectModel(QObject *parent = nullptr); + ~SimpleObjectModel(); + + QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + bool insert(const QModelIndex &parent, int row, const QString &text); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow) override; + +private: + Node *m_root; +}; + +SimpleObjectModel::SimpleObjectModel(QObject *parent) + : QAbstractListModel(parent) +{ + m_root = new Node; +} + +SimpleObjectModel::~SimpleObjectModel() +{} + +QModelIndex SimpleObjectModel::index(int row, int col, const QModelIndex &parent) const +{ + Node *parentItem; + if ( !parent.isValid() ) { + parentItem = static_cast<Node *>( m_root ); + } else { + parentItem = static_cast<Node *>( parent.internalPointer() ); + } + + if (row < 0 || parentItem->children.size() <= row) { + return QModelIndex(); + } + Node *childItem = parentItem->children[row]; + + return createIndex( row, col, childItem ); +} + +QModelIndex SimpleObjectModel::parent(const QModelIndex &index) const +{ + Node *childItem = static_cast<Node *>( index.internalPointer() ); + if( !childItem ) { + return QModelIndex(); + } + + Node *parent = childItem->parent; + Node *grandParent = parent->parent; + + int childRow = 0; + if( grandParent ) { + childRow = grandParent->children.indexOf( parent ); + } + + if ( parent == m_root ) { + return QModelIndex(); + } + return createIndex( childRow, 0, parent ); +} + +int SimpleObjectModel::rowCount(const QModelIndex &index) const +{ + Node *item = static_cast<Node *>( index.internalPointer() ); + if (!item) { + item = m_root; + } + + return item->children.count(); +} + +bool SimpleObjectModel::hasChildren(const QModelIndex &index) const +{ + Node *item = static_cast<Node *>( index.internalPointer() ); + if (!item) { + item = m_root; + } + + return !item->children.isEmpty(); +} + +QVariant SimpleObjectModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole) { + return QVariant(); + } + + Node *node = static_cast<Node *>( index.internalPointer() ); + if (!node) { + return QVariant(); + } + return node->label; +} + +bool SimpleObjectModel::insert(const QModelIndex &index, int row, const QString &text) +{ + if (row < 0) { + return false; + } + + Node *parent = static_cast<Node *>( index.internalPointer() ); + if (!parent) { + parent = m_root; + } + + if (row > parent->children.count()) { + return false; + } + + beginInsertRows(index, row, row); + Node *child = new Node; + child->parent = parent; + child->label = text; + parent->children.insert(row, child); + endInsertRows(); + + return true; +} + +bool SimpleObjectModel::removeRows(int row, int count, const QModelIndex &index) +{ + if (row < 0) { + return false; + } + + Node *parent = static_cast<Node *>( index.internalPointer() ); + if (!parent) { + parent = m_root; + } + + if (row >= parent->children.count()) { + return false; + } + + beginRemoveRows(index, row, row); + Node *child = parent->children.takeAt(row); + delete child; + endRemoveRows(); + + return true; +} + +bool SimpleObjectModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow) +{ + Node *sourceNode = static_cast<Node *>( sourceParent.internalPointer() ); + if (!sourceNode) { + sourceNode = m_root; + } + Node *destinationNode = static_cast<Node *>( destinationParent.internalPointer() ); + if (!destinationNode) { + destinationNode = m_root; + } + + const int sourceLast = sourceRow + count - 1; + + if (sourceNode != destinationNode) { + if (count <= 0 || sourceRow < 0 || sourceRow >= sourceNode->children.count() || + destinationRow < 0 || destinationRow > destinationNode->children.count()) { + return false; + } + + if (!beginMoveRows(sourceParent, sourceRow, sourceLast, destinationParent, destinationRow)) { + return false; + } + + Node *child = sourceNode->children.takeAt(sourceRow); + child->parent = destinationNode; + destinationNode->children.insert(destinationRow, child); + + endMoveRows(); + return true; + } + + if (count <= 0 || sourceRow == destinationRow || sourceRow < 0 || sourceRow >= destinationNode->children.count() || + destinationRow < 0 || destinationRow > destinationNode->children.count() || count - destinationRow > destinationNode->children.count() - sourceRow) { + return false; + } + + //beginMoveRows wants indexes before the source rows are removed from the old order + if (!beginMoveRows(sourceParent, sourceRow, sourceLast, destinationParent, destinationRow)) { + return false; + } + + if (sourceRow < destinationRow) { + for (int i = count - 1; i >= 0; --i) { + destinationNode->children.move(sourceRow + i, destinationRow - count + i); + } + } else { + for (int i = 0; i < count; ++i) { + destinationNode->children.move(sourceRow + i, destinationRow + i); + } + } + + endMoveRows(); + return true; +} + + class tst_KDescendantProxyModel : public QObject { Q_OBJECT QAbstractItemModel *createTree(const QString &prefix) { + /* + * |- parent1 + * | |- child1 + * | `- child2 + * `- parent2 + * |- child1 + * `- child2 + */ QStandardItemModel *model = new QStandardItemModel(); for (int i = 0; i < 2 ; i++) { QStandardItem *item = new QStandardItem(); @@ -33,6 +254,12 @@ void testChangeSeparator(); void testChangeInvisibleSeparator(); void testRemoveSeparator(); + + void testResetCollapsedModelContent(); + void testInsertInCollapsedModel(); + void testRemoveInCollapsedModel(); + void testMoveInsideCollapsed(); + void testExpandInsideCollapsed(); }; /// Tests that replacing the source model results in data getting changed @@ -202,6 +429,242 @@ } +void tst_KDescendantProxyModel::testResetCollapsedModelContent() +{ + auto model1 = createTree("FirstModel"); + KDescendantsProxyModel proxy; + proxy.setExpandsByDefault(false); + proxy.setSourceModel(model1); + QCOMPARE(proxy.rowCount(), 2); + + { + QStringList results = QStringList() + << "FirstModel0" + << "FirstModel1"; + QCOMPARE(proxy.rowCount(), results.count()); + for (int i = 0 ; i < proxy.rowCount() ; i++) { + QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); + } + } + { + QModelIndex idx = model1->index(0, 0); + proxy.expandSourceIndex(idx); + QStringList results = QStringList() + << "FirstModel0" + << "FirstModel0-0" + << "FirstModel0-1" + << "FirstModel1"; + QCOMPARE(proxy.rowCount(), results.count()); + for (int i = 0 ; i < proxy.rowCount() ; i++) { + QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); + } + } + { + QModelIndex idx = model1->index(1, 0); + proxy.expandSourceIndex(idx); + QStringList results = QStringList() + << "FirstModel0" + << "FirstModel0-0" + << "FirstModel0-1" + << "FirstModel1" + << "FirstModel1-0" + << "FirstModel1-1"; + QCOMPARE(proxy.rowCount(), results.count()); + for (int i = 0 ; i < proxy.rowCount() ; i++) { + QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); + } + } + + auto model2 = createTree("SecondModel"); + { + proxy.setSourceModel(model2); + QModelIndex idx = model2->index(0, 0); + proxy.expandSourceIndex(idx); + idx = model2->index(1, 0); + proxy.expandSourceIndex(idx); + QStringList results = QStringList() + << "SecondModel0" + << "SecondModel0-0" + << "SecondModel0-1" + << "SecondModel1" + << "SecondModel1-0" + << "SecondModel1-1"; + QCOMPARE(proxy.rowCount(), results.count()); + for (int i = 0 ; i < proxy.rowCount() ; i++) { + QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); + } + } + + delete model2; + delete model1; +} + +void tst_KDescendantProxyModel::testInsertInCollapsedModel() +{ + QStandardItemModel *model1 = static_cast<QStandardItemModel *>(createTree("Model")); + KDescendantsProxyModel proxy; + proxy.setExpandsByDefault(false); + proxy.setSourceModel(model1); + QCOMPARE(proxy.rowCount(), 2); + + QSignalSpy insertSpy(&proxy, &QAbstractItemModel::rowsInserted); + QCOMPARE(insertSpy.count(), 0); + + QStandardItem *parent = model1->item(0, 0); + QVERIFY(parent); + + QStandardItem *child = new QStandardItem(); + child->setData(QString(QStringLiteral("Model") + QString::number(0) + "-" + QString::number(2)), Qt::DisplayRole); + parent->appendRow(child); + + // Adding a child to the collapsed parent doesn't have an effect to the proxy + QCOMPARE(proxy.rowCount(), 2); + QCOMPARE(insertSpy.count(), 0); + + // If we expand everything inserted should be here + QModelIndex idx = model1->index(0, 0); + proxy.expandSourceIndex(idx); + + QCOMPARE(proxy.rowCount(), 5); + QCOMPARE(insertSpy.count(), 1); + + QCOMPARE(proxy.index(3, 0).data(Qt::DisplayRole).toString(), QStringLiteral("Model0-2")); + + // Add another child to the expanded node, now the proxy is affected immediately + child = new QStandardItem(); + child->setData(QString(QStringLiteral("Model") + QString::number(0) + "-" + QString::number(3)), Qt::DisplayRole); + parent->appendRow(child); + + QCOMPARE(proxy.rowCount(), 6); + QCOMPARE(insertSpy.count(), 2); +} + +void tst_KDescendantProxyModel::testRemoveInCollapsedModel() +{ + QStandardItemModel *model1 = static_cast<QStandardItemModel *>(createTree("Model")); + KDescendantsProxyModel proxy; + proxy.setExpandsByDefault(false); + proxy.setSourceModel(model1); + QCOMPARE(proxy.rowCount(), 2); + + QSignalSpy removeSpy(&proxy, &QAbstractItemModel::rowsRemoved); + QCOMPARE(removeSpy.count(), 0); + + QStandardItem *parent = model1->item(0, 0); + QVERIFY(parent); + + parent->removeRow(0); + + // Adding a child to the collapsed parent doesn't have an effect to the proxy + QCOMPARE(proxy.rowCount(), 2); + QCOMPARE(removeSpy.count(), 0); + + // If we expand everything inserted should be here + QModelIndex idx = model1->index(0, 0); + proxy.expandSourceIndex(idx); + + QCOMPARE(proxy.rowCount(), 3); + + QCOMPARE(proxy.index(1, 0).data(Qt::DisplayRole).toString(), QStringLiteral("Model0-1")); + parent->removeRow(0); + + QCOMPARE(proxy.rowCount(), 2); + QCOMPARE(removeSpy.count(), 1); + + idx = model1->index(1, 0); + proxy.expandSourceIndex(idx); + QCOMPARE(proxy.rowCount(), 4); +} + +void tst_KDescendantProxyModel::testMoveInsideCollapsed() +{ + SimpleObjectModel *model = new SimpleObjectModel; + model->insert(QModelIndex(), 0, QStringLiteral("Model0")); + model->insert(QModelIndex(), 1, QStringLiteral("Model1")); + model->insert(QModelIndex(), 2, QStringLiteral("Model2")); + + model->insert(model->index(0, 0, QModelIndex()), 0, QStringLiteral("Model0-0")); + model->insert(model->index(1, 0, QModelIndex()), 0, QStringLiteral("Model1-0")); + + QCOMPARE(model->rowCount(), 3); + + KDescendantsProxyModel proxy; + proxy.setExpandsByDefault(false); + proxy.setSourceModel(model); + QCOMPARE(proxy.rowCount(), 3); + + + QSignalSpy removeSpy(&proxy, &QAbstractItemModel::rowsRemoved); + QCOMPARE(removeSpy.count(), 0); + QSignalSpy insertSpy(&proxy, &QAbstractItemModel::rowsInserted); + QCOMPARE(insertSpy.count(), 0); + + model->moveRows(QModelIndex(), 2, 1, model->index(0, 0, QModelIndex()), 1); + + QCOMPARE(removeSpy.count(), 1); + + QVERIFY(!removeSpy.first()[0].value<QModelIndex>().isValid()); + QCOMPARE(removeSpy.first()[1].toInt(), 2); + QCOMPARE(removeSpy.first()[2].toInt(), 2); + + QCOMPARE(model->rowCount(), 2); + QCOMPARE(proxy.rowCount(), 2); + + model->moveRows(model->index(0, 0, QModelIndex()), 0, 1, QModelIndex(), 1); + QCOMPARE(insertSpy.count(), 1); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(proxy.rowCount(), 3); + + QVERIFY(!insertSpy.first()[0].value<QModelIndex>().isValid()); + QCOMPARE(insertSpy.first()[1].toInt(), 1); + QCOMPARE(insertSpy.first()[2].toInt(), 1); + + QModelIndex idx = model->index(0, 0); + proxy.expandSourceIndex(idx); + idx = model->index(1, 0); + proxy.expandSourceIndex(idx); + idx = model->index(2, 0); + proxy.expandSourceIndex(idx); + QStringList results = QStringList() + << "Model0" + << "Model2" + << "Model0-0" + << "Model1" + << "Model1-0"; + QCOMPARE(proxy.rowCount(), results.count()); + for (int i = 0 ; i < proxy.rowCount() ; i++) { + QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); + } +} + +void tst_KDescendantProxyModel::testExpandInsideCollapsed() +{ + SimpleObjectModel *model = new SimpleObjectModel; + model->insert(QModelIndex(), 0, QStringLiteral("Model0")); + model->insert(QModelIndex(), 1, QStringLiteral("Model1")); + model->insert(QModelIndex(), 2, QStringLiteral("Model2")); + + auto parentIndex = model->index(0, 0, QModelIndex()); + model->insert(parentIndex, 0, QStringLiteral("Model0-0")); + auto childIndex = model->index(0, 0, parentIndex); + model->insert(childIndex, 0, QStringLiteral("Model0-0-0")); + + QCOMPARE(model->rowCount(), 3); + + KDescendantsProxyModel proxy; + proxy.setExpandsByDefault(false); + proxy.setSourceModel(model); + QCOMPARE(proxy.rowCount(), 3); + + proxy.expandSourceIndex(childIndex); + QVERIFY(proxy.isSourceIndexExpanded(childIndex)); + QVERIFY(!proxy.isSourceIndexExpanded(parentIndex)); + QCOMPARE(proxy.rowCount(), 3); + proxy.expandSourceIndex(parentIndex); + QCOMPARE(proxy.rowCount(), 5); +} + + QTEST_MAIN(tst_KDescendantProxyModel) #include "kdescendantsproxymodeltest.moc" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/src/core/kdescendantsproxymodel.cpp new/kitemmodels-5.75.0/src/core/kdescendantsproxymodel.cpp --- old/kitemmodels-5.74.0/src/core/kdescendantsproxymodel.cpp 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/src/core/kdescendantsproxymodel.cpp 2020-10-04 11:53:39.000000000 +0200 @@ -41,6 +41,7 @@ void resetInternalData(); + void notifyhasSiblings(const QModelIndex &parent); void sourceRowsAboutToBeInserted(const QModelIndex &, int, int); void sourceRowsInserted(const QModelIndex &, int, int); void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int); @@ -59,6 +60,7 @@ QPair<int, int> m_removePair; QPair<int, int> m_insertPair; + bool m_expandsByDefault = true; bool m_ignoreNextLayoutAboutToBeChanged; bool m_ignoreNextLayoutChanged; bool m_relayouting; @@ -66,6 +68,9 @@ bool m_displayAncestorData; QString m_ancestorSeparator; + QSet<QPersistentModelIndex> m_expandedSourceIndexes; + QSet<QPersistentModelIndex> m_collapsedSourceIndexes; + QList<QPersistentModelIndex> m_layoutChangePersistentIndexes; QModelIndexList m_proxyIndexes; }; @@ -115,9 +120,19 @@ it = m_pendingParents.erase(it); continue; } + if (!q->isSourceIndexVisible(sourceParent)) { + // It's a collapsed node, or its parents are collapsed, ignore. + it = m_pendingParents.erase(it); + continue; + } + const int rowCount = q->sourceModel()->rowCount(sourceParent); - Q_ASSERT(rowCount > 0); + // A node can be marked as collapsed or expanded even if it doesn'0t have children + if (rowCount == 0) { + it = m_pendingParents.erase(it); + continue; + } const QPersistentModelIndex sourceIndex = q->sourceModel()->index(rowCount - 1, 0, sourceParent); Q_ASSERT(sourceIndex.isValid()); @@ -148,7 +163,9 @@ if (q->sourceModel()->hasChildren(child)) { Q_ASSERT(q->sourceModel()->rowCount(child) > 0); - newPendingParents.append(child); + if (q->isSourceIndexExpanded(child)) { + newPendingParents.append(child); + } } } } @@ -194,6 +211,143 @@ delete d_ptr; } +QHash<int, QByteArray> KDescendantsProxyModel::roleNames() const +{ + QHash<int, QByteArray> roleNames = QAbstractProxyModel::roleNames(); + + roleNames[LevelRole] = "kDescendantLevel"; + roleNames[ExpandableRole] = "kDescendantExpandable"; + roleNames[ExpandedRole] = "kDescendantExpanded"; + roleNames[HasSiblingsRole] = "kDescendantHasSiblings"; + return roleNames; +} + +void KDescendantsProxyModel::setExpandsByDefault(bool expand) +{ + if (d_ptr->m_expandsByDefault == expand) { + return; + } + + beginResetModel(); + d_ptr->m_expandsByDefault = expand; + d_ptr->m_expandedSourceIndexes.clear(); + d_ptr->m_collapsedSourceIndexes.clear(); + endResetModel(); +} + +bool KDescendantsProxyModel::expandsByDefault() const +{ + return d_ptr->m_expandsByDefault; +} + +bool KDescendantsProxyModel::isSourceIndexExpanded(const QModelIndex &sourceIndex) const +{ + // Root is always expanded + if (!sourceIndex.isValid()) { + return true; + } else if (d_ptr->m_expandsByDefault) { + return !d_ptr->m_collapsedSourceIndexes.contains(QPersistentModelIndex(sourceIndex)); + } else { + return d_ptr->m_expandedSourceIndexes.contains(QPersistentModelIndex(sourceIndex)); + } +} + +bool KDescendantsProxyModel::isSourceIndexVisible(const QModelIndex &sourceIndex) const +{ + // Root is always visible + if (!sourceIndex.isValid()) { + return true; + } + + QModelIndex index(sourceIndex); + do { + index = index.parent(); + if (!index.isValid()) { + return true; + } + } while (isSourceIndexExpanded(index)); + + return false; +} + +void KDescendantsProxyModel::expandSourceIndex(const QModelIndex &sourceIndex) +{ + if (!sourceIndex.isValid() || isSourceIndexExpanded(sourceIndex)) { + return; + } + + if (d_ptr->m_expandsByDefault) { + d_ptr->m_collapsedSourceIndexes.remove( QPersistentModelIndex(sourceIndex)); + } else { + d_ptr->m_expandedSourceIndexes << QPersistentModelIndex(sourceIndex); + } + + d_ptr->m_pendingParents << sourceIndex; + d_ptr->scheduleProcessPendingParents(); + Q_EMIT sourceIndexExpanded(sourceIndex); + + const QModelIndex index = mapFromSource(sourceIndex); + Q_EMIT dataChanged(index, index, {ExpandedRole}); +} + +void KDescendantsProxyModel::collapseSourceIndex(const QModelIndex &sourceIndex) +{ + if (!sourceIndex.isValid() || !isSourceIndexExpanded(sourceIndex)) { + return; + } + + const int row = mapFromSource(sourceIndex).row(); + const int rowStart = row + 1; + int rowEnd = row; + + QList <QModelIndex> toVisit = {sourceIndex}; + QSet <QModelIndex> visited; + while (!toVisit.isEmpty()) { + QModelIndex index = toVisit.takeLast(); + if (!visited.contains(index)) { + visited << index; + const int nRows = sourceModel()->rowCount(index); + rowEnd += nRows; + for (int i = 0; i < nRows; ++i) { + QModelIndex child = sourceModel()->index(i, 0, index); + if (isSourceIndexExpanded(child)) { + toVisit << child; + } + } + } + } + + if (d_ptr->m_expandsByDefault) { + d_ptr->m_collapsedSourceIndexes << QPersistentModelIndex(sourceIndex); + } else { + d_ptr->m_expandedSourceIndexes.remove( QPersistentModelIndex(sourceIndex)); + } + + { + Mapping::right_iterator it = d_ptr->m_mapping.rightLowerBound(rowStart); + const Mapping::right_iterator endIt = d_ptr->m_mapping.rightUpperBound(rowEnd); + + if (endIt != d_ptr->m_mapping.rightEnd()) + while (it != endIt) { + it = d_ptr->m_mapping.eraseRight(it); + } + else + while (it != d_ptr->m_mapping.rightUpperBound(rowEnd)) { + it = d_ptr->m_mapping.eraseRight(it); + } + } + + d_ptr->m_removePair = qMakePair(rowStart, rowEnd); + + beginRemoveRows(QModelIndex(), rowStart, rowEnd); + d_ptr->synchronousMappingRefresh(); + endRemoveRows(); + Q_EMIT sourceIndexCollapsed(sourceIndex); + + const QModelIndex ownIndex = mapFromSource(sourceIndex); + Q_EMIT dataChanged(ownIndex, ownIndex, {ExpandedRole}); +} + #if KITEMMODELS_BUILD_DEPRECATED_SINCE(4, 8) void KDescendantsProxyModel::setRootIndex(const QModelIndex &index) { @@ -291,6 +445,7 @@ } QAbstractProxyModel::setSourceModel(_sourceModel); + d_ptr->m_expandedSourceIndexes.clear(); if (_sourceModel) { for (int i = 0; i < int(sizeof modelSignals / sizeof * modelSignals); ++i) { @@ -426,6 +581,10 @@ const QModelIndex sourceParent = sourceIndex.parent(); Mapping::right_const_iterator result = end; + if (!isSourceIndexVisible(sourceIndex)) { + return QModelIndex(); + } + for (; it != end; ++it) { QModelIndex index = it.value(); bool found_block = false; @@ -500,6 +659,27 @@ sourceIndex = sourceIndex.parent(); } return displayData; + } else if (role == LevelRole) { + QModelIndex sourceIndex = mapToSource(index); + int level = 0; + while (sourceIndex.isValid()) { + sourceIndex = sourceIndex.parent(); + ++level; + } + return level; + } else if (role == ExpandableRole) { + QModelIndex sourceIndex = mapToSource(index); + return sourceModel()->hasChildren(sourceIndex); + } else if (role == ExpandedRole) { + return isSourceIndexExpanded(mapToSource(index)); + } else if (role == HasSiblingsRole) { + QModelIndex sourceIndex = mapToSource(index); + QList<bool> hasSibling; + while (sourceIndex.isValid()) { + hasSibling.prepend(sourceModel()->rowCount(sourceIndex.parent()) > sourceIndex.row() + 1); + sourceIndex = sourceIndex.parent(); + } + return QVariant::fromValue(hasSibling); } else { return sourceIndex.data(role); } @@ -525,10 +705,29 @@ return sourceModel()->flags(srcIndex); } +void KDescendantsProxyModelPrivate::notifyhasSiblings(const QModelIndex &parent) +{ + Q_Q(KDescendantsProxyModel); + + if (!parent.isValid()) { + return; + } + + QModelIndex localParent = q->mapFromSource(parent); + Q_EMIT q->dataChanged(localParent, localParent, {KDescendantsProxyModel::HasSiblingsRole}); + for (int i = 0; i < q->sourceModel()->rowCount(parent); ++i) { + notifyhasSiblings(q->sourceModel()->index(i, 0, parent)); + } +} + void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { Q_Q(KDescendantsProxyModel); + if (parent.isValid() && (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent))) { + return; + } + if (!q->sourceModel()->hasChildren(parent)) { Q_ASSERT(q->sourceModel()->rowCount(parent) == 0); // parent was not a parent before. @@ -548,7 +747,7 @@ Q_ASSERT(rowCount == start); static const int column = 0; QModelIndex idx = q->sourceModel()->index(rowCount - 1, column, parent); - while (q->sourceModel()->hasChildren(idx)) { + while (q->isSourceIndexExpanded(idx) && q->sourceModel()->hasChildren(idx)) { Q_ASSERT(q->sourceModel()->rowCount(idx) > 0); idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx); } @@ -564,7 +763,14 @@ void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end) { Q_Q(KDescendantsProxyModel); - + if (parent.isValid() && (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent))) { + const QModelIndex index = q->mapFromSource(parent); + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole}); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } + return; + } Q_ASSERT(q->sourceModel()->index(start, 0, parent).isValid()); const int rowCount = q->sourceModel()->rowCount(parent); @@ -573,9 +779,14 @@ const int difference = end - start + 1; if (rowCount == difference) { + const QModelIndex index = q->mapFromSource(parent); + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole}); // @p parent was not a parent before. m_pendingParents.append(parent); scheduleProcessPendingParents(); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } return; } @@ -636,11 +847,11 @@ // and we then insert D as a sibling of A below it, we need to remove the mapping for A, // and the row number used for D must take into account the descendants of A. - while (q->sourceModel()->hasChildren(indexAbove)) { + while (q->isSourceIndexExpanded(indexAbove) && q->sourceModel()->hasChildren(indexAbove)) { Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0); indexAbove = q->sourceModel()->index(q->sourceModel()->rowCount(indexAbove) - 1, column, indexAbove); } - Q_ASSERT(q->sourceModel()->rowCount(indexAbove) == 0); + Q_ASSERT(!q->isSourceIndexExpanded(indexAbove) || q->sourceModel()->rowCount(indexAbove) == 0); } Q_ASSERT(m_mapping.leftContains(indexAbove)); @@ -658,7 +869,8 @@ static const int column = 0; const QModelIndex idx = q->sourceModel()->index(row, column, parent); Q_ASSERT(idx.isValid()); - if (q->sourceModel()->hasChildren(idx)) { + + if (q->isSourceIndexExpanded(idx) && q->sourceModel()->hasChildren(idx)) { Q_ASSERT(q->sourceModel()->rowCount(idx) > 0); m_pendingParents.append(idx); } @@ -668,12 +880,22 @@ q->endInsertRows(); scheduleProcessPendingParents(); + const QModelIndex index = q->mapFromSource(parent); + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole}); + + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } } void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Q_Q(KDescendantsProxyModel); + if (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent)) { + return; + } + const int proxyStart = q->mapFromSource(q->sourceModel()->index(start, 0, parent)).row(); static const int column = 0; @@ -684,6 +906,11 @@ } const int proxyEnd = q->mapFromSource(idx).row(); + for(int i = start; i <= end; ++i) { + QModelIndex idx = q->sourceModel()->index(i, column, parent); + m_expandedSourceIndexes.remove(QPersistentModelIndex(idx)); + } + m_removePair = qMakePair(proxyStart, proxyEnd); q->beginRemoveRows(QModelIndex(), proxyStart, proxyEnd); @@ -710,6 +937,12 @@ Q_Q(KDescendantsProxyModel); Q_UNUSED(end) + if (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent)) { + const QModelIndex index = q->mapFromSource(parent); + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole}); + return; + } + const int rowCount = q->sourceModel()->rowCount(parent); const int proxyStart = m_removePair.first; @@ -738,6 +971,11 @@ if (rowCount != start || rowCount == 0) { q->endRemoveRows(); + const QModelIndex index = q->mapFromSource(parent); + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole}); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } return; } @@ -748,6 +986,9 @@ if (m_mapping.isEmpty()) { m_mapping.insert(newEnd, newEnd.row()); q->endRemoveRows(); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } return; } if (q->sourceModel()->hasChildren(newEnd)) { @@ -758,6 +999,9 @@ m_mapping.insert(newEnd, firstDeepestProxy - count); q->endRemoveRows(); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } return; } Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(proxyStart); @@ -774,6 +1018,9 @@ } m_mapping.insert(newEnd, proxyRow); q->endRemoveRows(); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } return; } else if (lowerBound == m_mapping.rightBegin()) { int proxyRow = rowCount - 1; @@ -784,6 +1031,9 @@ } m_mapping.insert(newEnd, proxyRow); q->endRemoveRows(); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } return; } const Mapping::right_iterator boundAbove = lowerBound - 1; @@ -797,6 +1047,9 @@ if (target == boundAbove.value()) { m_mapping.insert(newEnd, count + boundAbove.key() + newEnd.row() + 1); q->endRemoveRows(); + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } return; } count += (target.row() + 1); @@ -839,26 +1092,58 @@ } m_mapping.insert(newEnd, proxyRow + newEnd.row()); q->endRemoveRows(); + + const QModelIndex index = q->mapFromSource(parent); + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole}); + + if (!q->sourceModel()->hasChildren(parent)) { + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandedRole}); + } else if (q->sourceModel()->rowCount(parent) <= start) { + const QModelIndex index = q->mapFromSource(q->sourceModel()->index(q->sourceModel()->rowCount(parent) - 1, 0, parent)); + Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandedRole}); + } + if (start > 0) { + notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent)); + } } void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart) { - Q_UNUSED(srcParent) - Q_UNUSED(srcStart) - Q_UNUSED(srcEnd) - Q_UNUSED(destParent) + Q_Q(KDescendantsProxyModel); + Q_UNUSED(destStart) sourceLayoutAboutToBeChanged(); } void KDescendantsProxyModelPrivate::sourceRowsMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart) { + Q_Q(KDescendantsProxyModel); + Q_UNUSED(srcParent) Q_UNUSED(srcStart) Q_UNUSED(srcEnd) Q_UNUSED(destParent) Q_UNUSED(destStart) sourceLayoutChanged(); + + const QModelIndex index1 = q->mapFromSource(srcParent); + const QModelIndex index2 = q->mapFromSource(destParent); + Q_EMIT q->dataChanged(index1, index1, {KDescendantsProxyModel::ExpandableRole}); + if (index1 != index2) { + Q_EMIT q->dataChanged(index2, index2, {KDescendantsProxyModel::ExpandableRole}); + if (!q->sourceModel()->hasChildren(destParent)) { + Q_EMIT q->dataChanged(index2, index2, {KDescendantsProxyModel::ExpandableRole}); + } + } + const QModelIndex lastIndex = q->mapFromSource(q->sourceModel()->index(q->sourceModel()->rowCount(srcParent) - 1, 0, srcParent)); + Q_EMIT q->dataChanged(lastIndex, lastIndex, {KDescendantsProxyModel::ExpandableRole}); + + if (srcStart > 0) { + notifyhasSiblings(q->sourceModel()->index(srcStart - 1, 0, srcParent)); + } + if (destStart > 0) { + notifyhasSiblings(q->sourceModel()->index(destStart - 1, 0, destParent)); + } } void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset() @@ -935,14 +1220,27 @@ void KDescendantsProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { Q_Q(KDescendantsProxyModel); + // It is actually possible in a real world scenario that the source model emits dataChanged + // with invalid indexes when the source model is a QSortFilterProxyModel + // because QSortFilterProxyModel doesn't check for mapped index validity when its + // source model emitted dataChanged on a column QSortFilterProxyModel doesn't accept. + // See https://bugreports.qt.io/browse/QTBUG-86850 + if (!topLeft.isValid() || !bottomRight.isValid()) { + return; + } Q_ASSERT(topLeft.model() == q->sourceModel()); Q_ASSERT(bottomRight.model() == q->sourceModel()); + if (!q->isSourceIndexExpanded(topLeft.parent()) || !q->isSourceIndexVisible(topLeft.parent())) { + return; + } + const int topRow = topLeft.row(); const int bottomRow = bottomRight.row(); for (int i = topRow; i <= bottomRow; ++i) { const QModelIndex sourceTopLeft = q->sourceModel()->index(i, topLeft.column(), topLeft.parent()); + Q_ASSERT(sourceTopLeft.isValid()); const QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft); // TODO. If an index does not have any descendants, then we can emit in blocks of rows. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/src/core/kdescendantsproxymodel.h new/kitemmodels-5.75.0/src/core/kdescendantsproxymodel.h --- old/kitemmodels-5.74.0/src/core/kdescendantsproxymodel.h 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/src/core/kdescendantsproxymodel.h 2020-10-04 11:53:39.000000000 +0200 @@ -72,8 +72,25 @@ */ Q_PROPERTY(QString ancestorSeparator READ ancestorSeparator WRITE setAncestorSeparator NOTIFY ancestorSeparatorChanged) + /** + * If true, all the nodes in the whole tree will be expanded upon loading and all items + * of the source model will be shown in the proxy. + * The default value is true. + * @since 5.74 + */ + Q_PROPERTY(bool expandsByDefault READ expandsByDefault WRITE setExpandsByDefault NOTIFY expandsByDefaultChanged) + public: + enum AdditionalRoles { + // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) + // to define additional roles. + LevelRole = 0x14823F9A, + ExpandableRole = 0x1CA894AD, + ExpandedRole = 0x1E413DA4, + HasSiblingsRole = 0x1633CE0C + }; + /** * Creates a new descendant entities proxy model. * @@ -170,6 +187,46 @@ QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &) const override; int columnCount(const QModelIndex &index = QModelIndex()) const override; + QHash<int, QByteArray> roleNames() const override; + + /** + * If true, all the nodes in the whole tree will be expanded upon loading (default) + * @param expand whether we want everything expanded upon load + * @since 5.74 + */ + void setExpandsByDefault(bool expand); + + /** + * @returns true if all the tree nodes are expanded by default upon loading + * @since 5.74 + */ + bool expandsByDefault() const; + + /** + * @returns true if the source index is mapped in the proxy as expanded, therefore it will show its children + * @since 5.74 + */ + bool isSourceIndexExpanded(const QModelIndex &sourceIndex) const; + + /** + * @returns true if the source index is visible in the proxy, meaning all its parent hyerarchy is expanded. + * @since 5.74 + */ + bool isSourceIndexVisible(const QModelIndex &sourceIndex) const; + + /** + * Maps a source index as expanded in the proxy, all its children will become visible. + * @param sourceIndex an idex of the source model. + * @since 5.74 + */ + void expandSourceIndex(const QModelIndex &sourceIndex); + + /** + * Maps a source index as collapsed in the proxy, all its children will be hidden. + * @param sourceIndex an idex of the source model. + * @since 5.74 + */ + void collapseSourceIndex(const QModelIndex &sourceIndex); Qt::DropActions supportedDropActions() const override; @@ -183,6 +240,9 @@ void sourceModelChanged(); void displayAncestorDataChanged(); void ancestorSeparatorChanged(); + void expandsByDefaultChanged(bool expands); + void sourceIndexExpanded(const QModelIndex &sourceIndex); + void sourceIndexCollapsed(const QModelIndex &sourceIndex); private: Q_DECLARE_PRIVATE(KDescendantsProxyModel) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/src/qml/CMakeLists.txt new/kitemmodels-5.75.0/src/qml/CMakeLists.txt --- old/kitemmodels-5.74.0/src/qml/CMakeLists.txt 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/src/qml/CMakeLists.txt 2020-10-04 11:53:39.000000000 +0200 @@ -1,6 +1,7 @@ set(corebindings_SRCS plugin.cpp kconcatenaterowsproxymodel_qml.cpp + kdescendantsproxymodel_qml.cpp ksortfilterproxymodel.cpp ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/src/qml/kdescendantsproxymodel_qml.cpp new/kitemmodels-5.75.0/src/qml/kdescendantsproxymodel_qml.cpp --- old/kitemmodels-5.74.0/src/qml/kdescendantsproxymodel_qml.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.75.0/src/qml/kdescendantsproxymodel_qml.cpp 2020-10-04 11:53:39.000000000 +0200 @@ -0,0 +1,45 @@ +/* + SPDX-FileCopyrightText: 2020 Marco Martin <[email protected]> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "kdescendantsproxymodel_qml.h" +#include <QDebug> + +KDescendantsProxyModelQml::KDescendantsProxyModelQml(QObject *parent): + KDescendantsProxyModel(parent) +{ +} + +KDescendantsProxyModelQml::~KDescendantsProxyModelQml() +{} + +void KDescendantsProxyModelQml::expandChildren(int row) +{ + QModelIndex idx = mapToSource(index(row, 0)); + expandSourceIndex(idx); +} + +void KDescendantsProxyModelQml::collapseChildren(int row) +{ + QModelIndex idx = mapToSource(index(row, 0)); + collapseSourceIndex(idx); +} + +void KDescendantsProxyModelQml::toggleChildren(int row) +{ + QModelIndex sourceIndex = mapToSource(index(row, 0)); + + if (!sourceModel()->hasChildren(sourceIndex)) { + return; + } + + if (isSourceIndexExpanded(sourceIndex)) { + collapseSourceIndex(sourceIndex); + } else { + expandSourceIndex(sourceIndex); + } +} + +#include "moc_kdescendantsproxymodel_qml.cpp" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/src/qml/kdescendantsproxymodel_qml.h new/kitemmodels-5.75.0/src/qml/kdescendantsproxymodel_qml.h --- old/kitemmodels-5.74.0/src/qml/kdescendantsproxymodel_qml.h 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.75.0/src/qml/kdescendantsproxymodel_qml.h 2020-10-04 11:53:39.000000000 +0200 @@ -0,0 +1,26 @@ +/* + SPDX-FileCopyrightText: 2020 Marco Martin <[email protected]> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +//This class exposes KDescendantsProxyModel in a more QML friendly way + +#pragma once + +#include <QObject> +#include <QPointer> +#include <KDescendantsProxyModel> + +class KDescendantsProxyModelQml : public KDescendantsProxyModel +{ + Q_OBJECT + +public: + explicit KDescendantsProxyModelQml(QObject *parent = nullptr); + ~KDescendantsProxyModelQml(); + + Q_INVOKABLE void expandChildren(int row); + Q_INVOKABLE void collapseChildren(int row); + Q_INVOKABLE void toggleChildren(int row); +}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/src/qml/plugin.cpp new/kitemmodels-5.75.0/src/qml/plugin.cpp --- old/kitemmodels-5.74.0/src/qml/plugin.cpp 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/src/qml/plugin.cpp 2020-10-04 11:53:39.000000000 +0200 @@ -14,6 +14,7 @@ #include <KColumnHeadersModel> #include "ksortfilterproxymodel.h" #include "kconcatenaterowsproxymodel_qml.h" +#include "kdescendantsproxymodel_qml.h" void Plugin::initializeEngine(QQmlEngine *engine, const char *uri) { @@ -29,7 +30,7 @@ qmlRegisterType<QAbstractItemModel>(); #endif qmlRegisterExtendedType<KConcatenateRowsProxyModel,KConcatenateRowsProxyModelQml>(uri, 1, 0, "KConcatenateRowsProxyModel"); - qmlRegisterType<KDescendantsProxyModel>(uri, 1, 0, "KDescendantsProxyModel"); + qmlRegisterType<KDescendantsProxyModelQml>(uri, 1, 0, "KDescendantsProxyModel"); qmlRegisterType<KNumberModel>(uri, 1, 0, "KNumberModel"); qmlRegisterType<KColumnHeadersModel>(uri, 1, 0, "KColumnHeadersModel"); qmlRegisterType<KSortFilterProxyModel>(uri, 1, 0, "KSortFilterProxyModel"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/tests/proxymodeltestapp/CMakeLists.txt new/kitemmodels-5.75.0/tests/proxymodeltestapp/CMakeLists.txt --- old/kitemmodels-5.74.0/tests/proxymodeltestapp/CMakeLists.txt 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/tests/proxymodeltestapp/CMakeLists.txt 2020-10-04 11:53:39.000000000 +0200 @@ -16,6 +16,7 @@ lessthanwidget.cpp modelcommanderwidget.cpp matchcheckingwidget.cpp + descendantqmltree.cpp ) if(NOT EXCLUDE_DEPRECATED_BEFORE_AND_AT STREQUAL "CURRENT" AND EXCLUDE_DEPRECATED_BEFORE_AND_AT VERSION_LESS 5.65.0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/tests/proxymodeltestapp/descendantqmltree.cpp new/kitemmodels-5.75.0/tests/proxymodeltestapp/descendantqmltree.cpp --- old/kitemmodels-5.74.0/tests/proxymodeltestapp/descendantqmltree.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.75.0/tests/proxymodeltestapp/descendantqmltree.cpp 2020-10-04 11:53:39.000000000 +0200 @@ -0,0 +1,40 @@ +/* + This file is part of the proxy model test suite. + + SPDX-FileCopyrightText: 2020 Marco Martin <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-or-later +*/ + +#include "descendantqmltree.h" + +#include <QSplitter> +#include <QTreeView> +#include <QQuickWidget> +#include <QHBoxLayout> +#include <QLabel> +#include <QtQml> + +#include "dynamictreemodel.h" +#include "dynamictreewidget.h" +#include "kselectionproxymodel.h" + +DescendantQmlTreeWidget::DescendantQmlTreeWidget(QWidget *parent): QWidget(parent) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + QSplitter *splitter = new QSplitter(this); + layout->addWidget(splitter); + + m_rootModel = new DynamicTreeModel(this); + + new DynamicTreeWidget(m_rootModel, splitter); + + + qmlRegisterType<KSelectionProxyModel>("KF5ItemModels", 1, 0, "SelectionProxyModel"); + + QQuickWidget *quickView = new QQuickWidget(splitter); + + quickView->engine()->rootContext()->setContextProperty(QStringLiteral("_model"), m_rootModel); + + quickView->setSource(QUrl::fromLocalFile(QLatin1String(SRC_DIR "/tree.qml"))); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/tests/proxymodeltestapp/descendantqmltree.h new/kitemmodels-5.75.0/tests/proxymodeltestapp/descendantqmltree.h --- old/kitemmodels-5.74.0/tests/proxymodeltestapp/descendantqmltree.h 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.75.0/tests/proxymodeltestapp/descendantqmltree.h 2020-10-04 11:53:39.000000000 +0200 @@ -0,0 +1,27 @@ +/* + This file is part of the proxy model test suite. + + SPDX-FileCopyrightText: 2020 Marco Martin <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-or-later +*/ + +#ifndef DESCENDANTQMLTREE_H +#define DESCENDANTQMLTREE_H + +#include <QWidget> + +class QTreeView; + +class DynamicTreeModel; + +class DescendantQmlTreeWidget : public QWidget +{ +public: + DescendantQmlTreeWidget(QWidget *parent = nullptr); + +private: + DynamicTreeModel *m_rootModel; +}; + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/tests/proxymodeltestapp/mainwindow.cpp new/kitemmodels-5.75.0/tests/proxymodeltestapp/mainwindow.cpp --- old/kitemmodels-5.74.0/tests/proxymodeltestapp/mainwindow.cpp 2020-09-06 11:23:39.000000000 +0200 +++ new/kitemmodels-5.75.0/tests/proxymodeltestapp/mainwindow.cpp 2020-10-04 11:53:39.000000000 +0200 @@ -21,6 +21,7 @@ // #include "statesaverwidget.h" #include "proxymodeltestwidget.h" #include "proxyitemselectionwidget.h" +#include "descendantqmltree.h" #ifdef QT_SCRIPT_LIB #include "reparentingpmwidget.h" #endif @@ -57,6 +58,7 @@ #if KITEMMODELS_BUILD_DEPRECATED_SINCE(5, 65) tabWidget->addTab(new RecursiveFilterProxyWidget(), QStringLiteral("Recursive Filter")); #endif + tabWidget->addTab(new DescendantQmlTreeWidget(), QStringLiteral("QML Trees")); tabWidget->addTab(new LessThanWidget(), QStringLiteral("Less Than")); tabWidget->addTab(new ProxyModelTestWidget(), QStringLiteral("Proxy Model Test")); // tabWidget->addTab(new StateSaverWidget(), "State Saver Test"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitemmodels-5.74.0/tests/proxymodeltestapp/tree.qml new/kitemmodels-5.75.0/tests/proxymodeltestapp/tree.qml --- old/kitemmodels-5.74.0/tests/proxymodeltestapp/tree.qml 1970-01-01 01:00:00.000000000 +0100 +++ new/kitemmodels-5.75.0/tests/proxymodeltestapp/tree.qml 2020-10-04 11:53:39.000000000 +0200 @@ -0,0 +1,96 @@ + /* + SPDX-FileCopyrightText: 2029 Marco Martin <[email protected]> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ +import QtQuick 2.6 +import QtQuick.Layouts 1.4 +import QtQuick.Controls 2.2 as QQC2 +import org.kde.kirigami 2.13 as Kirigami +import org.kde.kitemmodels 1.0 +import org.kde.qqc2desktopstyle.private 1.0 as StylePrivate + + +QQC2.ScrollView { + id: root + width: 600 + height: 500 + + ListView { + spacing: 0 + clip: true + add: Transition { + NumberAnimation { + property: "opacity"; from: 0; to: 1; duration: 250 } + } + addDisplaced: Transition { + NumberAnimation { properties: "y"; duration: 250 } + } + remove: Transition { + NumberAnimation { + property: "opacity"; from: 1; to: 0; duration: 250 } + } + removeDisplaced: Transition { + NumberAnimation { properties: "y"; duration: 250 } + } + model: KDescendantsProxyModel { + id: descendantsModel + expandsByDefault: false + model: _model + } + + delegate: Kirigami.AbstractListItem { + id: delegate + highlighted: false + separatorVisible: false + + contentItem: RowLayout { + RowLayout { + Layout.topMargin: -delegate.topPadding + Layout.bottomMargin: -delegate.bottomPadding + Repeater { + + model: kDescendantLevel-1 + + delegate: StylePrivate.StyleItem { + Layout.preferredWidth: controlRoot.width//Kirigami.Units.gridUnit + Layout.fillHeight: true + visible: true + // control: controlRoot + elementType: "itembranchindicator" + properties: { + "isItem": false, + "hasSibling": kDescendantHasSiblings[modelData] + } + } + } + QQC2.Button { + id: controlRoot + Layout.preferredWidth: background.pixelMetric("treeviewindentation") + Layout.fillHeight: true + enabled: model.kDescendantExpandable + text: model.kDescendantExpanded ? "-" : "+" + onClicked: descendantsModel.toggleChildren(index) + background: StylePrivate.StyleItem { + id: styleitem + control: controlRoot + hover: controlRoot.hovered + elementType: "itembranchindicator" + on: model.kDescendantExpanded + properties: { + "isItem": true, + "hasChildren": model.kDescendantExpandable, + "hasSibling": model.kDescendantHasSiblings[model.kDescendantHasSiblings.length - 1] + } + } + } + } + + QQC2.Label { + Layout.fillWidth: true + text: model.display + } + } + } + } +}
