From 8f1868e7c36cb890c9d71dcd9d07d7019058cf94 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 23 Nov 2011 20:50:55 +0100 Subject: Add a parents parameter to layoutChange signals. This allows for more focussed notification of what part of the model has changed layout. The slots in the proxy models can be more optimized later. Change-Id: I1bd17465b4be6f8efdc107036db897c557fcb519 Reviewed-by: Olivier Goffart --- src/corelib/kernel/qabstractitemmodel.cpp | 12 +- src/corelib/kernel/qabstractitemmodel.h | 4 +- .../qabstractitemmodel/tst_qabstractitemmodel.cpp | 172 +++++++++++++++++++++ .../modeltest/dynamictreemodel.cpp | 52 +++++++ .../integrationtests/modeltest/dynamictreemodel.h | 17 ++ 5 files changed, 253 insertions(+), 4 deletions(-) diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index fbeb59a553..cbbb20a8c9 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -1306,7 +1306,7 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, */ /*! - \fn void QAbstractItemModel::layoutAboutToBeChanged() + \fn void QAbstractItemModel::layoutAboutToBeChanged(const QList &parents = QList()) \since 4.2 This signal is emitted just before the layout of a model is changed. @@ -1316,11 +1316,15 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, Subclasses should update any persistent model indexes after emitting layoutAboutToBeChanged(). + The optional @p parents parameter is used to give a more specific notification + about what parts of the layout of the model are changing. An empty list indicates + a change to the layout of the entire model. + \sa layoutChanged(), changePersistentIndex() */ /*! - \fn void QAbstractItemModel::layoutChanged() + \fn void QAbstractItemModel::layoutChanged(const QList &parents = QList()) This signal is emitted whenever the layout of items exposed by the model has changed; for example, when the model has been sorted. When this signal @@ -1332,6 +1336,10 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, altering the structure of the data you expose to views, and emit layoutChanged() after changing the layout. + The optional @p parents parameter is used to give a more specific notification + about what parts of the layout of the model are changing. An empty list indicates + a change to the layout of the entire model. + Subclasses should update any persistent model indexes before emitting layoutChanged(). In other words, when the structure changes: diff --git a/src/corelib/kernel/qabstractitemmodel.h b/src/corelib/kernel/qabstractitemmodel.h index 97c5b58482..0aa8144602 100644 --- a/src/corelib/kernel/qabstractitemmodel.h +++ b/src/corelib/kernel/qabstractitemmodel.h @@ -233,8 +233,8 @@ public: Q_SIGNALS: void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QSet &roles = QSet()); void headerDataChanged(Qt::Orientation orientation, int first, int last); - void layoutChanged(); - void layoutAboutToBeChanged(); + void layoutChanged(const QList &parents = QList()); + void layoutAboutToBeChanged(const QList &parents = QList()); #if !defined(Q_MOC_RUN) && !defined(qdoc) private: // can only be emitted by QAbstractItemModel diff --git a/tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp index 4b09b455ad..c704329717 100644 --- a/tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp +++ b/tests/auto/corelib/kernel/qabstractitemmodel/tst_qabstractitemmodel.cpp @@ -112,6 +112,8 @@ private slots: void testDataChanged(); + void testChildrenLayoutsChanged(); + private: DynamicTreeModel *m_model; }; @@ -1841,6 +1843,176 @@ void tst_QAbstractItemModel::testDataChanged() QVERIFY(thirdRoles.contains(CustomRoleModel::Custom1)); } +Q_DECLARE_METATYPE(QList) + +class SignalArgumentChecker : public QObject +{ + Q_OBJECT +public: + SignalArgumentChecker(const QModelIndex &p1, const QModelIndex &p2, QObject *parent = 0) + : QObject(parent), m_p1(p1), m_p2(p2), m_p1Persistent(p1), m_p2Persistent(p2) + { + connect(p1.model(), SIGNAL(layoutAboutToBeChanged(QList)), SLOT(layoutAboutToBeChanged(QList))); + connect(p1.model(), SIGNAL(layoutChanged(QList)), SLOT(layoutChanged(QList))); + } + +private slots: + void layoutAboutToBeChanged(const QList &parents) + { + QCOMPARE(parents.size(), 2); + QVERIFY(parents.first() != parents.at(1)); + QVERIFY(parents.contains(m_p1)); + QVERIFY(parents.contains(m_p2)); + } + + void layoutChanged(const QList &parents) + { + QCOMPARE(parents.size(), 2); + QVERIFY(parents.first() != parents.at(1)); + QVERIFY(parents.contains(m_p1Persistent)); + QVERIFY(parents.contains(m_p2Persistent)); + QVERIFY(!parents.contains(m_p2)); // Has changed + } + +private: + QModelIndex m_p1; + QModelIndex m_p2; + QPersistentModelIndex m_p1Persistent; + QPersistentModelIndex m_p2Persistent; +}; + +void tst_QAbstractItemModel::testChildrenLayoutsChanged() +{ + DynamicTreeModel model; + + ModelInsertCommand *insertCommand = new ModelInsertCommand(&model, this); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + insertCommand = new ModelInsertCommand(&model, this); + insertCommand->setAncestorRowNumbers(QList() << 2); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + insertCommand = new ModelInsertCommand(&model, this); + insertCommand->setAncestorRowNumbers(QList() << 5); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + qRegisterMetaType >(); + + { + const QModelIndex p1 = model.index(2, 0); + const QModelIndex p2 = model.index(5, 0); + + const QPersistentModelIndex p1FirstPersistent = model.index(0, 0, p1); + const QPersistentModelIndex p1LastPersistent = model.index(9, 0, p1); + const QPersistentModelIndex p2FirstPersistent = model.index(0, 0, p2); + const QPersistentModelIndex p2LastPersistent = model.index(9, 0, p2); + + QVERIFY(p1.isValid()); + QVERIFY(p2.isValid()); + + QCOMPARE(model.rowCount(), 10); + QCOMPARE(model.rowCount(p1), 10); + QCOMPARE(model.rowCount(p2), 10); + + QSignalSpy beforeSpy(&model, SIGNAL(layoutAboutToBeChanged(QList))); + QSignalSpy afterSpy(&model, SIGNAL(layoutChanged(QList))); + + ModelChangeChildrenLayoutsCommand *changeCommand = new ModelChangeChildrenLayoutsCommand(&model, this); + changeCommand->setAncestorRowNumbers(QList() << 2); + changeCommand->setSecondAncestorRowNumbers(QList() << 5); + changeCommand->doCommand(); + + QCOMPARE(beforeSpy.size(), 1); + QCOMPARE(afterSpy.size(), 1); + + const QVariantList beforeSignal = beforeSpy.first(); + const QVariantList afterSignal = afterSpy.first(); + QCOMPARE(beforeSignal.size(), 1); + QCOMPARE(afterSignal.size(), 1); + + const QList beforeParents = beforeSignal.first().value >(); + QCOMPARE(beforeParents.size(), 2); + QVERIFY(beforeParents.first() != beforeParents.at(1)); + QVERIFY(beforeParents.contains(p1)); + QVERIFY(beforeParents.contains(p2)); + + const QList afterParents = afterSignal.first().value >(); + QCOMPARE(afterParents.size(), 2); + QVERIFY(afterParents.first() != afterParents.at(1)); + QVERIFY(afterParents.contains(p1)); + QVERIFY(afterParents.contains(p2)); + + // The first will be the last, and the lest will be the first. + QVERIFY(p1FirstPersistent.row() == 1); + QVERIFY(p1LastPersistent.row() == 0); + QVERIFY(p2FirstPersistent.row() == 9); + QVERIFY(p2LastPersistent.row() == 8); + + } + + insertCommand = new ModelInsertCommand(&model, this); + insertCommand->setAncestorRowNumbers(QList() << 5 << 4); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + delete insertCommand; + + // Even when p2 itself is moved around, signal emission remains correct for its children. + { + const QModelIndex p1 = model.index(5, 0); + const QModelIndex p2 = model.index(4, 0, p1); + + QVERIFY(p1.isValid()); + QVERIFY(p2.isValid()); + + QCOMPARE(model.rowCount(), 10); + QCOMPARE(model.rowCount(p1), 10); + QCOMPARE(model.rowCount(p2), 10); + + const QPersistentModelIndex p1Persistent = p1; + const QPersistentModelIndex p2Persistent = p2; + + const QPersistentModelIndex p1FirstPersistent = model.index(0, 0, p1); + const QPersistentModelIndex p1LastPersistent = model.index(9, 0, p1); + const QPersistentModelIndex p2FirstPersistent = model.index(0, 0, p2); + const QPersistentModelIndex p2LastPersistent = model.index(9, 0, p2); + + QSignalSpy beforeSpy(&model, SIGNAL(layoutAboutToBeChanged(QList))); + QSignalSpy afterSpy(&model, SIGNAL(layoutChanged(QList))); + + // Because the arguments in the signal are persistent, we need to check them for the aboutToBe + // case at emission time - before they get updated. + SignalArgumentChecker checker(p1, p2); + + ModelChangeChildrenLayoutsCommand *changeCommand = new ModelChangeChildrenLayoutsCommand(&model, this); + changeCommand->setAncestorRowNumbers(QList() << 5); + changeCommand->setSecondAncestorRowNumbers(QList() << 5 << 4); + changeCommand->doCommand(); + + // p2 has been moved. + QCOMPARE(p2Persistent.row(), p2.row() + 1); + + QCOMPARE(beforeSpy.size(), 1); + QCOMPARE(afterSpy.size(), 1); + + const QVariantList beforeSignal = beforeSpy.first(); + const QVariantList afterSignal = afterSpy.first(); + QCOMPARE(beforeSignal.size(), 1); + QCOMPARE(afterSignal.size(), 1); + + QVERIFY(p1FirstPersistent.row() == 1); + QVERIFY(p1LastPersistent.row() == 0); + QVERIFY(p2FirstPersistent.row() == 9); + QVERIFY(p2LastPersistent.row() == 8); + } +} QTEST_MAIN(tst_QAbstractItemModel) #include "tst_qabstractitemmodel.moc" diff --git a/tests/auto/integrationtests/modeltest/dynamictreemodel.cpp b/tests/auto/integrationtests/modeltest/dynamictreemodel.cpp index 2f8bb0a730..5ab37ab112 100644 --- a/tests/auto/integrationtests/modeltest/dynamictreemodel.cpp +++ b/tests/auto/integrationtests/modeltest/dynamictreemodel.cpp @@ -338,3 +338,55 @@ void ModelResetCommandFixed::emitPostSignal() m_model->endResetModel(); } +ModelChangeChildrenLayoutsCommand::ModelChangeChildrenLayoutsCommand(DynamicTreeModel* model, QObject* parent) + : ModelChangeCommand(model, parent) +{ + +} + +void ModelChangeChildrenLayoutsCommand::doCommand() +{ + const QPersistentModelIndex parent1 = findIndex(m_rowNumbers); + const QPersistentModelIndex parent2 = findIndex(m_secondRowNumbers); + + QList parents; + parents << parent1; + parents << parent2; + + emit m_model->layoutAboutToBeChanged(parents); + + int rowSize1 = -1; + int rowSize2 = -1; + + for (int column = 0; column < m_numCols; ++column) + { + { + QList &l = m_model->m_childItems[parent1.internalId()][column]; + rowSize1 = l.size(); + l.prepend(l.takeLast()); + } + { + QList &l = m_model->m_childItems[parent2.internalId()][column]; + rowSize2 = l.size(); + l.append(l.takeFirst()); + } + } + + foreach (const QModelIndex &idx, m_model->persistentIndexList()) { + if (idx.parent() == parent1) { + if (idx.row() == rowSize1 - 1) { + m_model->changePersistentIndex(idx, m_model->createIndex(0, idx.column(), idx.internalPointer())); + } else { + m_model->changePersistentIndex(idx, m_model->createIndex(idx.row() + 1, idx.column(), idx.internalPointer())); + } + } else if (idx.parent() == parent2) { + if (idx.row() == 0) { + m_model->changePersistentIndex(idx, m_model->createIndex(rowSize2 - 1, idx.column(), idx.internalPointer())); + } else { + m_model->changePersistentIndex(idx, m_model->createIndex(idx.row() - 1, idx.column(), idx.internalPointer())); + } + } + } + + emit m_model->layoutChanged(parents); +} diff --git a/tests/auto/integrationtests/modeltest/dynamictreemodel.h b/tests/auto/integrationtests/modeltest/dynamictreemodel.h index 81ef80cc40..6f52d78588 100644 --- a/tests/auto/integrationtests/modeltest/dynamictreemodel.h +++ b/tests/auto/integrationtests/modeltest/dynamictreemodel.h @@ -89,6 +89,7 @@ private: friend class ModelMoveCommand; friend class ModelResetCommand; friend class ModelResetCommandFixed; + friend class ModelChangeChildrenLayoutsCommand; }; @@ -193,5 +194,21 @@ public: }; +class ModelChangeChildrenLayoutsCommand : public ModelChangeCommand +{ + Q_OBJECT +public: + ModelChangeChildrenLayoutsCommand(DynamicTreeModel *model, QObject *parent); + + virtual ~ModelChangeChildrenLayoutsCommand() {} + + virtual void doCommand(); + + void setSecondAncestorRowNumbers( QList rows ) { m_secondRowNumbers = rows; } + +protected: + QList m_secondRowNumbers; + int m_destRow; +}; #endif -- cgit v1.2.3