Fix emission of QQmlListModel::rowsAboutToBeXxx() signals

Call beginInsertRows(), beginMoveRows() and beginRemoveRows() before
the change to ensure that rowsAboutToBeInserted(), rowsAboutToBeMoved()
and rowsAboutToBeRemoved() get emitted before the change as appropriate.

NOTE: This patch solves the problem for the most common use case, when
ListModel is used without WorkerScript. QQmlListModelWorkerAgent needs
similar changes in order to fix the signals when ListModel is used with
WorkerScript (QTBUG-39321).

Task-number: QTBUG-39279
Change-Id: Idec5167d70b242f6f7d8b7cff008e130afc62505
Reviewed-by: Alan Alpert <aalpert@blackberry.com>
This commit is contained in:
J-P Nurmi 2014-05-27 14:14:52 +02:00 committed by The Qt Project
parent b5cab0515b
commit 0306626a4d
3 changed files with 172 additions and 4 deletions

View File

@ -1699,13 +1699,20 @@ void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &r
}
}
void QQmlListModel::emitItemsAboutToBeRemoved(int index, int count)
{
if (count <= 0 || !m_mainThread)
return;
beginRemoveRows(QModelIndex(), index, index + count - 1);
}
void QQmlListModel::emitItemsRemoved(int index, int count)
{
if (count <= 0)
return;
if (m_mainThread) {
beginRemoveRows(QModelIndex(), index, index + count - 1);
endRemoveRows();
emit countChanged();
} else {
@ -1716,13 +1723,20 @@ void QQmlListModel::emitItemsRemoved(int index, int count)
}
}
void QQmlListModel::emitItemsAboutToBeInserted(int index, int count)
{
if (count <= 0 || !m_mainThread)
return;
beginInsertRows(QModelIndex(), index, index + count - 1);
}
void QQmlListModel::emitItemsInserted(int index, int count)
{
if (count <= 0)
return;
if (m_mainThread) {
beginInsertRows(QModelIndex(), index, index + count - 1);
endInsertRows();
emit countChanged();
} else {
@ -1731,13 +1745,20 @@ void QQmlListModel::emitItemsInserted(int index, int count)
}
}
void QQmlListModel::emitItemsAboutToBeMoved(int from, int to, int n)
{
if (n <= 0 || !m_mainThread)
return;
beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
}
void QQmlListModel::emitItemsMoved(int from, int to, int n)
{
if (n <= 0)
return;
if (m_mainThread) {
beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
endMoveRows();
} else {
int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
@ -1877,6 +1898,8 @@ void QQmlListModel::clear()
{
int cleared = count();
emitItemsAboutToBeRemoved(0, cleared);
if (m_dynamicRoles) {
for (int i=0 ; i < m_modelObjects.count() ; ++i)
delete m_modelObjects[i];
@ -1909,6 +1932,8 @@ void QQmlListModel::remove(QQmlV4Function *args)
return;
}
emitItemsAboutToBeRemoved(index, removeCount);
if (m_dynamicRoles) {
for (int i=0 ; i < removeCount ; ++i)
delete m_modelObjects[index+i];
@ -1957,6 +1982,7 @@ void QQmlListModel::insert(QQmlV4Function *args)
QV4::ScopedObject argObject(scope);
int objectArrayLength = objectArray->getLength();
emitItemsAboutToBeInserted(index, objectArrayLength);
for (int i=0 ; i < objectArrayLength ; ++i) {
argObject = objectArray->getIndexed(i);
@ -1968,6 +1994,8 @@ void QQmlListModel::insert(QQmlV4Function *args)
}
emitItemsInserted(index, objectArrayLength);
} else if (argObject) {
emitItemsAboutToBeInserted(index, 1);
if (m_dynamicRoles) {
m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
} else {
@ -2006,6 +2034,8 @@ void QQmlListModel::move(int from, int to, int n)
return;
}
emitItemsAboutToBeMoved(from, to, n);
if (m_dynamicRoles) {
int realFrom = from;
@ -2061,6 +2091,8 @@ void QQmlListModel::append(QQmlV4Function *args)
int objectArrayLength = objectArray->getLength();
int index = count();
emitItemsAboutToBeInserted(index, objectArrayLength);
for (int i=0 ; i < objectArrayLength ; ++i) {
argObject = objectArray->getIndexed(i);
@ -2077,9 +2109,12 @@ void QQmlListModel::append(QQmlV4Function *args)
if (m_dynamicRoles) {
index = m_modelObjects.count();
emitItemsAboutToBeInserted(index, 1);
m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
} else {
index = m_listModel->append(argObject, args->engine());
index = m_listModel->elementCount();
emitItemsAboutToBeInserted(index, 1);
m_listModel->append(argObject, args->engine());
}
emitItemsInserted(index, 1);
@ -2174,6 +2209,7 @@ void QQmlListModel::set(int index, const QQmlV4Handle &handle)
if (index == count()) {
emitItemsAboutToBeInserted(index, 1);
if (m_dynamicRoles) {
m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this));

View File

@ -144,8 +144,11 @@ private:
static QQmlListModel *createWithOwner(QQmlListModel *newOwner);
void emitItemsChanged(int index, int count, const QVector<int> &roles);
void emitItemsAboutToBeRemoved(int index, int count);
void emitItemsRemoved(int index, int count);
void emitItemsAboutToBeInserted(int index, int count);
void emitItemsInserted(int index, int count);
void emitItemsAboutToBeMoved(int from, int to, int n);
void emitItemsMoved(int from, int to, int n);
};

View File

@ -132,6 +132,7 @@ private slots:
void empty_element_warning_data();
void datetime();
void datetime_data();
void about_to_be_signals();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@ -1306,6 +1307,134 @@ void tst_qqmllistmodel::datetime()
QVERIFY(expected == dtResult);
}
class RowTester : public QObject
{
Q_OBJECT
public:
RowTester(QAbstractItemModel *model) : QObject(model), model(model)
{
reset();
connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted()));
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted()));
connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved()));
connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved()));
connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsAboutToBeMoved()));
connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsMoved()));
}
void reset()
{
rowsAboutToBeInsertedCalls = 0;
rowsAboutToBeInsertedCount = 0;
rowsInsertedCalls = 0;
rowsInsertedCount = 0;
rowsAboutToBeRemovedCalls = 0;
rowsAboutToBeRemovedCount = 0;
rowsRemovedCalls = 0;
rowsRemovedCount = 0;
rowsAboutToBeMovedCalls = 0;
rowsAboutToBeMovedData.clear();
rowsMovedCalls = 0;
rowsMovedData.clear();
}
int rowsAboutToBeInsertedCalls;
int rowsAboutToBeInsertedCount;
int rowsInsertedCalls;
int rowsInsertedCount;
int rowsAboutToBeRemovedCalls;
int rowsAboutToBeRemovedCount;
int rowsRemovedCalls;
int rowsRemovedCount;
int rowsAboutToBeMovedCalls;
QVariantList rowsAboutToBeMovedData;
int rowsMovedCalls;
QVariantList rowsMovedData;
private slots:
void rowsAboutToBeInserted()
{
rowsAboutToBeInsertedCalls++;
rowsAboutToBeInsertedCount = model->rowCount();
}
void rowsInserted()
{
rowsInsertedCalls++;
rowsInsertedCount = model->rowCount();
}
void rowsAboutToBeRemoved()
{
rowsAboutToBeRemovedCalls++;
rowsAboutToBeRemovedCount = model->rowCount();
}
void rowsRemoved()
{
rowsRemovedCalls++;
rowsRemovedCount = model->rowCount();
}
void rowsAboutToBeMoved()
{
rowsAboutToBeMovedCalls++;
for (int i = 0; i < model->rowCount(); ++i)
rowsAboutToBeMovedData += model->data(model->index(i, 0), 0);
}
void rowsMoved()
{
rowsMovedCalls++;
for (int i = 0; i < model->rowCount(); ++i)
rowsMovedData += model->data(model->index(i, 0), 0);
}
private:
QAbstractItemModel *model;
};
void tst_qqmllistmodel::about_to_be_signals()
{
QQmlEngine engine;
QQmlListModel model;
QQmlEngine::setContextForObject(&model,engine.rootContext());
RowTester tester(&model);
QQmlExpression e1(engine.rootContext(), &model, "{append({'test':0})}");
e1.evaluate();
QCOMPARE(tester.rowsAboutToBeInsertedCalls, 1);
QCOMPARE(tester.rowsAboutToBeInsertedCount, 0);
QCOMPARE(tester.rowsInsertedCalls, 1);
QCOMPARE(tester.rowsInsertedCount, 1);
QQmlExpression e2(engine.rootContext(), &model, "{append({'test':1})}");
e2.evaluate();
QCOMPARE(tester.rowsAboutToBeInsertedCalls, 2);
QCOMPARE(tester.rowsAboutToBeInsertedCount, 1);
QCOMPARE(tester.rowsInsertedCalls, 2);
QCOMPARE(tester.rowsInsertedCount, 2);
QQmlExpression e3(engine.rootContext(), &model, "{move(0, 1, 1)}");
e3.evaluate();
QCOMPARE(tester.rowsAboutToBeMovedCalls, 1);
QCOMPARE(tester.rowsAboutToBeMovedData, QVariantList() << 0.0 << 1.0);
QCOMPARE(tester.rowsMovedCalls, 1);
QCOMPARE(tester.rowsMovedData, QVariantList() << 1.0 << 0.0);
QQmlExpression e4(engine.rootContext(), &model, "{remove(0, 2)}");
e4.evaluate();
QCOMPARE(tester.rowsAboutToBeRemovedCalls, 1);
QCOMPARE(tester.rowsAboutToBeRemovedCount, 2);
QCOMPARE(tester.rowsRemovedCalls, 1);
QCOMPARE(tester.rowsRemovedCount, 0);
}
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"