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 <ogoffart@woboq.com>
This commit is contained in:
Stephen Kelly 2011-11-23 20:50:55 +01:00 committed by Qt by Nokia
parent 11bf824c03
commit 8f1868e7c3
5 changed files with 253 additions and 4 deletions

View File

@ -1306,7 +1306,7 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent,
*/
/*!
\fn void QAbstractItemModel::layoutAboutToBeChanged()
\fn void QAbstractItemModel::layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>())
\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<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>())
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:

View File

@ -233,8 +233,8 @@ public:
Q_SIGNALS:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QSet<int> &roles = QSet<int>());
void headerDataChanged(Qt::Orientation orientation, int first, int last);
void layoutChanged();
void layoutAboutToBeChanged();
void layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>());
void layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>());
#if !defined(Q_MOC_RUN) && !defined(qdoc)
private: // can only be emitted by QAbstractItemModel

View File

@ -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<QPersistentModelIndex>)
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<QPersistentModelIndex>)), SLOT(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
connect(p1.model(), SIGNAL(layoutChanged(QList<QPersistentModelIndex>)), SLOT(layoutChanged(QList<QPersistentModelIndex>)));
}
private slots:
void layoutAboutToBeChanged(const QList<QPersistentModelIndex> &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<QPersistentModelIndex> &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<int>() << 2);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
insertCommand->doCommand();
insertCommand = new ModelInsertCommand(&model, this);
insertCommand->setAncestorRowNumbers(QList<int>() << 5);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
insertCommand->doCommand();
qRegisterMetaType<QList<QPersistentModelIndex> >();
{
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<QPersistentModelIndex>)));
QSignalSpy afterSpy(&model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)));
ModelChangeChildrenLayoutsCommand *changeCommand = new ModelChangeChildrenLayoutsCommand(&model, this);
changeCommand->setAncestorRowNumbers(QList<int>() << 2);
changeCommand->setSecondAncestorRowNumbers(QList<int>() << 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<QPersistentModelIndex> beforeParents = beforeSignal.first().value<QList<QPersistentModelIndex> >();
QCOMPARE(beforeParents.size(), 2);
QVERIFY(beforeParents.first() != beforeParents.at(1));
QVERIFY(beforeParents.contains(p1));
QVERIFY(beforeParents.contains(p2));
const QList<QPersistentModelIndex> afterParents = afterSignal.first().value<QList<QPersistentModelIndex> >();
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<int>() << 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<QPersistentModelIndex>)));
QSignalSpy afterSpy(&model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)));
// 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<int>() << 5);
changeCommand->setSecondAncestorRowNumbers(QList<int>() << 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"

View File

@ -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<QPersistentModelIndex> 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<qint64> &l = m_model->m_childItems[parent1.internalId()][column];
rowSize1 = l.size();
l.prepend(l.takeLast());
}
{
QList<qint64> &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);
}

View File

@ -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<int> rows ) { m_secondRowNumbers = rows; }
protected:
QList<int> m_secondRowNumbers;
int m_destRow;
};
#endif