StatusQ(MovableModel): Add ability to restore and bypass original order of source model

This commit is contained in:
Michał Cieślak 2024-02-16 11:01:40 +01:00 committed by Michał
parent e46f6c311c
commit f747791f50
3 changed files with 152 additions and 35 deletions

View File

@ -23,6 +23,7 @@ public:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void detach(); Q_INVOKABLE void detach();
Q_INVOKABLE void attach();
Q_INVOKABLE void move(int from, int to, int count = 1); Q_INVOKABLE void move(int from, int to, int count = 1);
Q_INVOKABLE QVector<int> order() const; Q_INVOKABLE QVector<int> order() const;
@ -38,6 +39,8 @@ protected slots:
private: private:
QPointer<QAbstractItemModel> m_sourceModel; QPointer<QAbstractItemModel> m_sourceModel;
void connectSignalsForAttachedState();
bool m_detached = false; bool m_detached = false;
std::vector<QPersistentModelIndex> m_indexes; std::vector<QPersistentModelIndex> m_indexes;
}; };

View File

@ -20,41 +20,7 @@ void MovableModel::setSourceModel(QAbstractItemModel* sourceModel)
disconnect(m_sourceModel, nullptr, this, nullptr); disconnect(m_sourceModel, nullptr, this, nullptr);
m_sourceModel = sourceModel; m_sourceModel = sourceModel;
connectSignalsForAttachedState();
if (sourceModel != nullptr) {
connect(sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this,
&MovableModel::beginInsertRows);
connect(sourceModel, &QAbstractItemModel::rowsInserted, this,
&MovableModel::endInsertRows);
connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this,
&MovableModel::beginRemoveRows);
connect(sourceModel, &QAbstractItemModel::rowsRemoved, this,
&MovableModel::endRemoveRows);
connect(sourceModel, &QAbstractItemModel::rowsAboutToBeMoved, this,
&MovableModel::beginMoveRows);
connect(sourceModel, &QAbstractItemModel::rowsMoved, this,
&MovableModel::endMoveRows);
connect(sourceModel, &QAbstractItemModel::dataChanged, this,
&MovableModel::dataChanged);
connect(sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this,
&MovableModel::layoutAboutToBeChanged);
connect(sourceModel, &QAbstractItemModel::layoutChanged, this,
&MovableModel::layoutChanged);
connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, this,
&MovableModel::beginResetModel);
connect(sourceModel, &QAbstractItemModel::modelReset, this,
&MovableModel::endResetModel);
}
emit sourceModelChanged(); emit sourceModelChanged();
@ -213,6 +179,23 @@ void MovableModel::detach()
emit detachedChanged(); emit detachedChanged();
} }
void MovableModel::attach()
{
if (!m_detached || m_sourceModel == nullptr)
return;
emit layoutAboutToBeChanged();
auto sourceModel = m_sourceModel;
disconnect(m_sourceModel, nullptr, this, nullptr);
connectSignalsForAttachedState();
resetInternalData();
emit layoutChanged();
}
void MovableModel::move(int from, int to, int count) void MovableModel::move(int from, int to, int count)
{ {
const int rows = rowCount(); const int rows = rowCount();
@ -278,3 +261,42 @@ void MovableModel::resetInternalData()
emit detachedChanged(); emit detachedChanged();
} }
} }
void MovableModel::connectSignalsForAttachedState()
{
if (m_sourceModel == nullptr)
return;
connect(m_sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this,
&MovableModel::beginInsertRows);
connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this,
&MovableModel::endInsertRows);
connect(m_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this,
&MovableModel::beginRemoveRows);
connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this,
&MovableModel::endRemoveRows);
connect(m_sourceModel, &QAbstractItemModel::rowsAboutToBeMoved, this,
&MovableModel::beginMoveRows);
connect(m_sourceModel, &QAbstractItemModel::rowsMoved, this,
&MovableModel::endMoveRows);
connect(m_sourceModel, &QAbstractItemModel::dataChanged, this,
&MovableModel::dataChanged);
connect(m_sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this,
&MovableModel::layoutAboutToBeChanged);
connect(m_sourceModel, &QAbstractItemModel::layoutChanged, this,
&MovableModel::layoutChanged);
connect(m_sourceModel, &QAbstractItemModel::modelAboutToBeReset, this,
&MovableModel::beginResetModel);
connect(m_sourceModel, &QAbstractItemModel::modelReset, this,
&MovableModel::endResetModel);
}

View File

@ -571,6 +571,98 @@ private slots:
// QTest::failOnWarning(QRegularExpression(".?")); // Qt 6.3 // QTest::failOnWarning(QRegularExpression(".?")); // Qt 6.3
sourceModel.move(0, 0, 2); sourceModel.move(0, 0, 2);
} }
void resetSourceTest() {
QQmlEngine engine;
auto source1 = R"([
{ "name": "A", "subname": "a1" },
{ "name": "A", "subname": "a2" },
{ "name": "B", "subname": "b1" },
{ "name": "C", "subname": "c1" },
{ "name": "C", "subname": "c2" },
{ "name": "C", "subname": "c3" }
])";
auto source2 = R"([
{ "name_": "A", "subname_": "a1" },
{ "name_": "A", "subname_": "a2" }
])";
ListModelWrapper sourceModel1(engine, source1);
ListModelWrapper sourceModel2(engine, source2);
MovableModel model;
model.setSourceModel(sourceModel1);
model.detach();
QCOMPARE(model.detached(), true);
ModelSignalsSpy signalsSpy(&model);
QSignalSpy detachChangedSpy(&model, &MovableModel::detachedChanged);
model.setSourceModel(sourceModel2);
QCOMPARE(signalsSpy.count(), 2);
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
QCOMPARE(detachChangedSpy.count(), 1);
QCOMPARE(model.detached(), false);
QCOMPARE(model.rowCount(), 2);
QVERIFY(isSame(&model, sourceModel2));
}
void attachTest()
{
QQmlEngine engine;
auto source = R"([
{ "name": "A", "subname": "a1" },
{ "name": "A", "subname": "a2" },
{ "name": "B", "subname": "b1" },
{ "name": "C", "subname": "c1" },
{ "name": "C", "subname": "c2" },
{ "name": "C", "subname": "c3" }
])";
ListModelWrapper sourceModel(engine, source);
MovableModel model;
{
ModelSignalsSpy signalsSpy(&model);
model.attach();
QCOMPARE(signalsSpy.count(), 0);
}
model.setSourceModel(sourceModel);
{
ModelSignalsSpy signalsSpy(&model);
model.attach();
QCOMPARE(signalsSpy.count(), 0);
}
model.detach();
sourceModel.move(0, 2, 2);
QVERIFY(!isSame(&model, sourceModel));
ModelSignalsSpy signalsSpy(&model);
QSignalSpy detachChangedSpy(&model, &MovableModel::detachedChanged);
model.attach();
QCOMPARE(signalsSpy.count(), 2);
QCOMPARE(signalsSpy.layoutAboutToBeChangedSpy.count(), 1);
QCOMPARE(signalsSpy.layoutChangedSpy.count(), 1);
QCOMPARE(detachChangedSpy.count(), 1);
QCOMPARE(model.detached(), false);
QVERIFY(isSame(&model, sourceModel));
}
}; };
QTEST_MAIN(TestMovableModel) QTEST_MAIN(TestMovableModel)