feat(WritableProxyModel): Add syncedRemoval flag to allow source to remove edited rows
This commit is contained in:
parent
67b81e1953
commit
204bfb30b6
|
@ -26,6 +26,9 @@ class WritableProxyModel : public QAbstractProxyModel
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool dirty READ dirty NOTIFY dirtyChanged)
|
||||
//If true, the removals are synced with the source model. Removing an edited item from source will also remove it from proxy
|
||||
//If false, eemoving an edited item from source will not remove it from proxy. It will become a newly inserted row
|
||||
Q_PROPERTY(bool syncedRemovals READ syncedRemovals WRITE setSyncedRemovals NOTIFY syncedRemovalsChanged)
|
||||
|
||||
public:
|
||||
explicit WritableProxyModel(QObject* parent = nullptr);
|
||||
|
@ -48,6 +51,7 @@ public:
|
|||
Q_INVOKABLE bool set(int at, const QVariantMap& data);
|
||||
|
||||
bool dirty() const;
|
||||
bool syncedRemovals() const;
|
||||
|
||||
//QAbstractProxyModel overrides
|
||||
void setSourceModel(QAbstractItemModel* sourceModel) override;
|
||||
|
@ -75,9 +79,11 @@ public:
|
|||
|
||||
signals:
|
||||
void dirtyChanged();
|
||||
void syncedRemovalsChanged();
|
||||
|
||||
private:
|
||||
void setDirty(bool flag);
|
||||
void setSyncedRemovals(bool syncedRemovals);
|
||||
|
||||
void onSourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles);
|
||||
void onRowsAboutToBeInserted(const QModelIndex& parent, int start, int end);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QAbstractItemModelTester>
|
||||
#endif
|
||||
#include <memory>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
template <typename T>
|
||||
|
@ -23,6 +24,8 @@ public:
|
|||
QSet<QPersistentModelIndex> removedRows;
|
||||
QVector<int> proxyToSourceRowMapping;
|
||||
bool dirty{false};
|
||||
bool syncedRemovals{false};
|
||||
bool syncedRemovalsInitialized{false};
|
||||
|
||||
void setData(const QModelIndex& index, const QVariant& value, int role);
|
||||
template<typename T>
|
||||
|
@ -244,11 +247,14 @@ int WritableProxyModelPrivate::countOffset() const
|
|||
|
||||
void WritableProxyModelPrivate::moveFromCacheToInserted(const QModelIndex& sourceIndex)
|
||||
{
|
||||
if (!q.sourceModel())
|
||||
if (!q.sourceModel() || syncedRemovals)
|
||||
return;
|
||||
|
||||
|
||||
//User updated this row. Move it in inserted rows. We shouldn't delete it
|
||||
auto proxyIndex = insertedRows.insert(q.mapFromSource(sourceIndex), cache.take(sourceIndex));
|
||||
// syncedRemovalsInitialized cannot be changed after this point
|
||||
syncedRemovalsInitialized = true;
|
||||
|
||||
auto itemData = q.sourceModel()->itemData(sourceIndex);
|
||||
for (auto it = itemData.begin(); it != itemData.end(); ++it)
|
||||
{
|
||||
|
@ -283,7 +289,7 @@ void WritableProxyModelPrivate::checkForDirtyRemoval(const QModelIndex& sourceIn
|
|||
{
|
||||
if (cachedData.contains(role) && cachedData[role] == q.sourceModel()->data(sourceIndex, role))
|
||||
cachedData.remove(role);
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedData.isEmpty()) {
|
||||
cache.remove(sourceIndex);
|
||||
|
@ -625,6 +631,27 @@ void WritableProxyModel::setDirty(bool flag)
|
|||
emit dirtyChanged();
|
||||
}
|
||||
|
||||
bool WritableProxyModel::syncedRemovals() const
|
||||
{
|
||||
return d->syncedRemovals;
|
||||
}
|
||||
|
||||
void WritableProxyModel::setSyncedRemovals(bool syncedRemovals)
|
||||
{
|
||||
if (d->syncedRemovalsInitialized)
|
||||
{
|
||||
qWarning() << "WritableProxyModel: syncedRemovals cannot be updated after it has been initialized";
|
||||
return;
|
||||
}
|
||||
|
||||
if (syncedRemovals == d->syncedRemovals)
|
||||
return;
|
||||
|
||||
d->syncedRemovals = syncedRemovals;
|
||||
d->syncedRemovalsInitialized = true;
|
||||
emit syncedRemovalsChanged();
|
||||
}
|
||||
|
||||
void WritableProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
|
||||
{
|
||||
if (sourceModel == QAbstractProxyModel::sourceModel())
|
||||
|
@ -992,6 +1019,12 @@ void WritableProxyModel::onRowsRemoved(const QModelIndex& parent, int first, int
|
|||
void WritableProxyModel::onModelAboutToBeReset()
|
||||
{
|
||||
beginResetModel();
|
||||
if (d->syncedRemovals)
|
||||
{
|
||||
d->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto iter = d->cache.begin(); iter != d->cache.end();)
|
||||
{
|
||||
auto key = iter.key();
|
||||
|
@ -1006,6 +1039,7 @@ void WritableProxyModel::onModelReset()
|
|||
d->clearInvalidatedCache();
|
||||
d->createProxyToSourceRowMap();
|
||||
resetInternalData();
|
||||
d->checkForDirtyRemoval({}, {});
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
|
|
@ -732,6 +732,66 @@ private slots:
|
|||
QCOMPARE(model.data(model.index(3, 0), 1), {});
|
||||
}
|
||||
|
||||
void updaedDataIsNotKeptAfterSourceRemove()
|
||||
{
|
||||
WritableProxyModel model;
|
||||
QAbstractItemModelTester tester(&model);
|
||||
|
||||
TestSourceModel sourceModel({
|
||||
{ "title", { "Token 1", "Token 2", "Token3" }},
|
||||
{ "communityId", { "community_1", "community_2", "community_3" }}});
|
||||
|
||||
model.setSourceModel(&sourceModel);
|
||||
model.setProperty("syncedRemovals", true);
|
||||
|
||||
QSignalSpy rowsRemovedSpy(&model, &WritableProxyModel::rowsRemoved);
|
||||
QSignalSpy modelResetSpy(&model, &WritableProxyModel::modelReset);
|
||||
QSignalSpy dataChangedSpy(&model, &WritableProxyModel::dataChanged);
|
||||
QSignalSpy rowsInsertedSpy(&model, &WritableProxyModel::rowsInserted);
|
||||
|
||||
QCOMPARE(model.dirty(), false);
|
||||
QCOMPARE(model.syncedRemovals(), true);
|
||||
|
||||
model.setData(model.index(0, 0), "Token 1.1", 0);
|
||||
|
||||
QCOMPARE(model.dirty(), true);
|
||||
QCOMPARE(model.rowCount(), 3);
|
||||
|
||||
QCOMPARE(model.data(model.index(0, 0), 0), "Token 1.1");
|
||||
QCOMPARE(dataChangedSpy.count(), 1);
|
||||
|
||||
sourceModel.remove(0);
|
||||
|
||||
QCOMPARE(model.dirty(), false);
|
||||
QCOMPARE(model.rowCount(), 2);
|
||||
QCOMPARE(model.data(model.index(0, 0), 0), "Token 2");
|
||||
|
||||
QCOMPARE(rowsRemovedSpy.count(), 1);
|
||||
QCOMPARE(rowsRemovedSpy.first().at(1), 0);
|
||||
QCOMPARE(rowsRemovedSpy.first().at(2), 0);
|
||||
QCOMPARE(modelResetSpy.count(), 0);
|
||||
QCOMPARE(rowsInsertedSpy.count(), 0);
|
||||
|
||||
model.setData(model.index(0, 0), "Token 2.1", 0);
|
||||
|
||||
QCOMPARE(model.dirty(), true);
|
||||
|
||||
QCOMPARE(model.data(model.index(0, 0), 0), "Token 2.1");
|
||||
|
||||
sourceModel.reset({
|
||||
{ "title", { "Token 3", "Token 4" }},
|
||||
{ "communityId", { "community_3", "community_4" }}
|
||||
});
|
||||
|
||||
QCOMPARE(model.dirty(), false);
|
||||
QCOMPARE(model.rowCount(), 2);
|
||||
|
||||
QCOMPARE(rowsRemovedSpy.count(), 1);
|
||||
QCOMPARE(modelResetSpy.count(), 1);
|
||||
QCOMPARE(dataChangedSpy.count(), 2);
|
||||
QCOMPARE(rowsInsertedSpy.count(), 0);
|
||||
}
|
||||
|
||||
void dataIsAccessibleAfterSourceModelMove()
|
||||
{
|
||||
WritableProxyModel model;
|
||||
|
|
Loading…
Reference in New Issue