feat: Add delayed property
This commit is contained in:
parent
770789ee48
commit
36befddf5d
|
@ -26,7 +26,12 @@ namespace qqsfpm {
|
|||
*/
|
||||
|
||||
QQmlSortFilterProxyModel::QQmlSortFilterProxyModel(QObject *parent) :
|
||||
QSortFilterProxyModel(parent)
|
||||
QSortFilterProxyModel(parent),
|
||||
#ifdef SFPM_DELAYED
|
||||
m_delayed(true)
|
||||
#else
|
||||
m_delayed(false)
|
||||
#endif
|
||||
{
|
||||
connect(this, &QAbstractProxyModel::sourceModelChanged, this, &QQmlSortFilterProxyModel::updateRoles);
|
||||
connect(this, &QAbstractItemModel::modelReset, this, &QQmlSortFilterProxyModel::updateRoles);
|
||||
|
@ -55,6 +60,30 @@ int QQmlSortFilterProxyModel::count() const
|
|||
return rowCount();
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlproperty int SortFilterProxyModel::delayed
|
||||
|
||||
Delay the execution of filters, sorters and proxyRoles until the next event loop.
|
||||
This can be used as an optimization when multiple filters, sorters or proxyRoles are changed in a single event loop.
|
||||
They will be executed once in a single batch at the next event loop instead of being executed in multiple sequential batches.
|
||||
|
||||
By default, the SortFilterProxyModel is not delayed, unless the SFPM_DELAYED is defined at compile time.
|
||||
*/
|
||||
|
||||
bool QQmlSortFilterProxyModel::delayed() const
|
||||
{
|
||||
return m_delayed;
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::setDelayed(bool delayed)
|
||||
{
|
||||
if (m_delayed == delayed)
|
||||
return;
|
||||
|
||||
m_delayed = delayed;
|
||||
Q_EMIT delayedChanged();
|
||||
}
|
||||
|
||||
const QString& QQmlSortFilterProxyModel::filterRoleName() const
|
||||
{
|
||||
return m_filterRoleName;
|
||||
|
@ -114,7 +143,7 @@ void QQmlSortFilterProxyModel::setFilterValue(const QVariant& filterValue)
|
|||
return;
|
||||
|
||||
m_filterValue = filterValue;
|
||||
invalidateFilter();
|
||||
queueInvalidateFilter();
|
||||
Q_EMIT filterValueChanged();
|
||||
}
|
||||
|
||||
|
@ -153,7 +182,7 @@ void QQmlSortFilterProxyModel::setAscendingSortOrder(bool ascendingSortOrder)
|
|||
|
||||
m_ascendingSortOrder = ascendingSortOrder;
|
||||
Q_EMIT ascendingSortOrderChanged();
|
||||
invalidate();
|
||||
queueInvalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -362,14 +391,40 @@ void QQmlSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
|
|||
QSortFilterProxyModel::setSourceModel(sourceModel);
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::queueInvalidateFilter()
|
||||
{
|
||||
if (m_delayed) {
|
||||
if (!m_invalidateFilterQueued && !m_invalidateQueued) {
|
||||
m_invalidateFilterQueued = true;
|
||||
QMetaObject::invokeMethod(this, "invalidateFilter", Qt::QueuedConnection);
|
||||
}
|
||||
} else {
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::invalidateFilter()
|
||||
{
|
||||
if (m_completed)
|
||||
m_invalidateFilterQueued = false;
|
||||
if (m_completed && !m_invalidateQueued)
|
||||
QSortFilterProxyModel::invalidateFilter();
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::queueInvalidate()
|
||||
{
|
||||
if (m_delayed) {
|
||||
if (!m_invalidateQueued) {
|
||||
m_invalidateQueued = true;
|
||||
QMetaObject::invokeMethod(this, "invalidate", Qt::QueuedConnection);
|
||||
}
|
||||
} else {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::invalidate()
|
||||
{
|
||||
m_invalidateQueued = false;
|
||||
if (m_completed)
|
||||
QSortFilterProxyModel::invalidate();
|
||||
}
|
||||
|
@ -410,7 +465,7 @@ void QQmlSortFilterProxyModel::updateSortRole()
|
|||
if (!sortRoles.empty())
|
||||
{
|
||||
setSortRole(sortRoles.first());
|
||||
invalidate();
|
||||
queueInvalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,10 +489,24 @@ void QQmlSortFilterProxyModel::onDataChanged(const QModelIndex& topLeft, const Q
|
|||
Q_EMIT dataChanged(topLeft, bottomRight, m_proxyRoleNumbers);
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::emitProxyRolesChanged()
|
||||
void QQmlSortFilterProxyModel::queueInvalidateProxyRoles()
|
||||
{
|
||||
invalidate();
|
||||
Q_EMIT dataChanged(index(0,0), index(rowCount() - 1, columnCount() - 1), m_proxyRoleNumbers);
|
||||
queueInvalidate();
|
||||
if (m_delayed) {
|
||||
if (!m_invalidateProxyRolesQueued) {
|
||||
m_invalidateProxyRolesQueued = true;
|
||||
QMetaObject::invokeMethod(this, "invalidateProxyRoles", Qt::QueuedConnection);
|
||||
}
|
||||
} else {
|
||||
invalidateProxyRoles();
|
||||
}
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::invalidateProxyRoles()
|
||||
{
|
||||
m_invalidateProxyRolesQueued = false;
|
||||
if (m_completed)
|
||||
Q_EMIT dataChanged(index(0,0), index(rowCount() - 1, columnCount() - 1), m_proxyRoleNumbers);
|
||||
}
|
||||
|
||||
QVariantMap QQmlSortFilterProxyModel::modelDataMap(const QModelIndex& modelIndex) const
|
||||
|
@ -451,42 +520,42 @@ QVariantMap QQmlSortFilterProxyModel::modelDataMap(const QModelIndex& modelIndex
|
|||
|
||||
void QQmlSortFilterProxyModel::onFilterAppended(Filter* filter)
|
||||
{
|
||||
connect(filter, &Filter::invalidated, this, &QQmlSortFilterProxyModel::invalidateFilter);
|
||||
this->invalidateFilter();
|
||||
connect(filter, &Filter::invalidated, this, &QQmlSortFilterProxyModel::queueInvalidateFilter);
|
||||
queueInvalidateFilter();
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::onFilterRemoved(Filter* filter)
|
||||
{
|
||||
Q_UNUSED(filter)
|
||||
invalidateFilter();
|
||||
queueInvalidateFilter();
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::onFiltersCleared()
|
||||
{
|
||||
invalidateFilter();
|
||||
queueInvalidateFilter();
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::onSorterAppended(Sorter* sorter)
|
||||
{
|
||||
connect(sorter, &Sorter::invalidated, this, &QQmlSortFilterProxyModel::invalidate);
|
||||
invalidate();
|
||||
connect(sorter, &Sorter::invalidated, this, &QQmlSortFilterProxyModel::queueInvalidate);
|
||||
queueInvalidate();
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::onSorterRemoved(Sorter* sorter)
|
||||
{
|
||||
Q_UNUSED(sorter)
|
||||
invalidate();
|
||||
queueInvalidate();
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::onSortersCleared()
|
||||
{
|
||||
invalidate();
|
||||
queueInvalidate();
|
||||
}
|
||||
|
||||
void QQmlSortFilterProxyModel::onProxyRoleAppended(ProxyRole *proxyRole)
|
||||
{
|
||||
beginResetModel();
|
||||
connect(proxyRole, &ProxyRole::invalidated, this, &QQmlSortFilterProxyModel::emitProxyRolesChanged);
|
||||
connect(proxyRole, &ProxyRole::invalidated, this, &QQmlSortFilterProxyModel::queueInvalidateProxyRoles);
|
||||
connect(proxyRole, &ProxyRole::namesAboutToBeChanged, this, &QQmlSortFilterProxyModel::beginResetModel);
|
||||
connect(proxyRole, &ProxyRole::namesChanged, this, &QQmlSortFilterProxyModel::endResetModel);
|
||||
endResetModel();
|
||||
|
|
|
@ -22,6 +22,7 @@ class QQmlSortFilterProxyModel : public QSortFilterProxyModel,
|
|||
Q_INTERFACES(qqsfpm::ProxyRoleContainer)
|
||||
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
Q_PROPERTY(bool delayed READ delayed WRITE setDelayed NOTIFY delayedChanged)
|
||||
|
||||
Q_PROPERTY(QString filterRoleName READ filterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged)
|
||||
Q_PROPERTY(QString filterPattern READ filterPattern WRITE setFilterPattern NOTIFY filterPatternChanged)
|
||||
|
@ -49,6 +50,9 @@ public:
|
|||
|
||||
int count() const;
|
||||
|
||||
bool delayed() const;
|
||||
void setDelayed(bool delayed);
|
||||
|
||||
const QString& filterRoleName() const;
|
||||
void setFilterRoleName(const QString& filterRoleName);
|
||||
|
||||
|
@ -90,6 +94,7 @@ public:
|
|||
|
||||
Q_SIGNALS:
|
||||
void countChanged();
|
||||
void delayedChanged();
|
||||
|
||||
void filterRoleNameChanged();
|
||||
void filterPatternSyntaxChanged();
|
||||
|
@ -107,7 +112,9 @@ protected Q_SLOTS:
|
|||
void resetInternalData();
|
||||
|
||||
private Q_SLOTS:
|
||||
void queueInvalidateFilter();
|
||||
void invalidateFilter();
|
||||
void queueInvalidate();
|
||||
void invalidate();
|
||||
void updateRoleNames();
|
||||
void updateFilterRole();
|
||||
|
@ -115,7 +122,8 @@ private Q_SLOTS:
|
|||
void updateRoles();
|
||||
void initRoles();
|
||||
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles);
|
||||
void emitProxyRolesChanged();
|
||||
void queueInvalidateProxyRoles();
|
||||
void invalidateProxyRoles();
|
||||
|
||||
private:
|
||||
QVariantMap modelDataMap(const QModelIndex& modelIndex) const;
|
||||
|
@ -132,6 +140,7 @@ private:
|
|||
void onProxyRoleRemoved(ProxyRole *proxyRole) override;
|
||||
void onProxyRolesCleared() override;
|
||||
|
||||
bool m_delayed;
|
||||
QString m_filterRoleName;
|
||||
QVariant m_filterValue;
|
||||
QString m_sortRoleName;
|
||||
|
@ -140,6 +149,10 @@ private:
|
|||
QHash<int, QByteArray> m_roleNames;
|
||||
QHash<int, QPair<ProxyRole*, QString>> m_proxyRoleMap;
|
||||
QVector<int> m_proxyRoleNumbers;
|
||||
|
||||
bool m_invalidateFilterQueued = false;
|
||||
bool m_invalidateQueued = false;
|
||||
bool m_invalidateProxyRolesQueued = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -32,4 +32,5 @@ DISTFILES += \
|
|||
tst_filtercontainers.qml \
|
||||
tst_regexprole.qml \
|
||||
tst_filtersorter.qml \
|
||||
tst_filterrole.qml
|
||||
tst_filterrole.qml \
|
||||
tst_delayed.qml
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
import QtQuick 2.0
|
||||
import QtQml 2.2
|
||||
import QtTest 1.1
|
||||
import SortFilterProxyModel 0.2
|
||||
import SortFilterProxyModel.Test 0.2
|
||||
|
||||
Item {
|
||||
ListModel {
|
||||
id: testModel1
|
||||
ListElement{ role1: 1 }
|
||||
}
|
||||
SortFilterProxyModel {
|
||||
id: testFilterProxyModel
|
||||
sourceModel: testModel1
|
||||
property int foo: 1
|
||||
filters: [
|
||||
ExpressionFilter {
|
||||
id: expressionFilter
|
||||
property var w: ({count : 0}) // wrap count in a js object so modifying it doesn't bind it in the expression
|
||||
expression: {
|
||||
++w.count;
|
||||
testFilterProxyModel.foo;
|
||||
return true;
|
||||
}
|
||||
},
|
||||
ValueFilter {
|
||||
roleName: "role1"
|
||||
value: testFilterProxyModel.foo
|
||||
},
|
||||
ValueFilter {
|
||||
roleName: "role1"
|
||||
value: testFilterProxyModel.foo
|
||||
}
|
||||
]
|
||||
sorters: RoleSorter {
|
||||
roleName: "role1"
|
||||
sortOrder: testFilterProxyModel.foo === 1 ? Qt.AscendingOrder : Qt.DescendingOrder
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: testModel2
|
||||
ListElement{ role1: 1 }
|
||||
ListElement{ role1: 2 }
|
||||
}
|
||||
SortFilterProxyModel {
|
||||
id: testSorterProxyModel
|
||||
sourceModel: testModel2
|
||||
property bool foo: true
|
||||
sorters: [
|
||||
ExpressionSorter {
|
||||
id: expressionSorter
|
||||
property var w: ({count : 0}) // wrap count in a js object so modifying it doesn't bind it in the expression
|
||||
expression: {
|
||||
++w.count;
|
||||
testSorterProxyModel.foo;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "role1"
|
||||
sortOrder: testSorterProxyModel.foo ? Qt.AscendingOrder : Qt.DescendingOrder
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "role1"
|
||||
sortOrder: testSorterProxyModel.foo ? Qt.AscendingOrder : Qt.DescendingOrder
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: testRolesProxyModel
|
||||
sourceModel: testModel1
|
||||
property bool foo: true
|
||||
proxyRoles: [
|
||||
StaticRole {
|
||||
name: "display"
|
||||
value: 5
|
||||
},
|
||||
ExpressionRole {
|
||||
id: expressionRole
|
||||
name: "expressionRole"
|
||||
property var w: ({count : 0}) // wrap count in a js object so modifying it doesn't bind it in the expression
|
||||
expression: {
|
||||
++w.count;
|
||||
return testRolesProxyModel.foo;
|
||||
}
|
||||
},
|
||||
StaticRole {
|
||||
name: "role1"
|
||||
value: testRolesProxyModel.foo
|
||||
},
|
||||
StaticRole {
|
||||
name: "role2"
|
||||
value: testRolesProxyModel.foo
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
SignalSpy {
|
||||
id: dataChangedSpy
|
||||
target: testRolesProxyModel
|
||||
signalName: "dataChanged"
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
id: instantiator
|
||||
model: testRolesProxyModel
|
||||
delegate: QtObject { property bool foo: model.expressionRole; property bool foo2: model.expressionRole }
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "DelayedTest"
|
||||
|
||||
function test_directFilters() {
|
||||
testFilterProxyModel.delayed = false;
|
||||
expressionFilter.w.count = 0;
|
||||
testFilterProxyModel.foo = 2;
|
||||
compare(testFilterProxyModel.count, 0);
|
||||
compare(expressionFilter.w.count, 4);
|
||||
wait(0);
|
||||
compare(testFilterProxyModel.count, 0);
|
||||
compare(expressionFilter.w.count, 4);
|
||||
}
|
||||
|
||||
function test_delayedFilters() {
|
||||
testFilterProxyModel.delayed = false;
|
||||
testFilterProxyModel.foo = 2;
|
||||
compare(testFilterProxyModel.count, 0);
|
||||
testFilterProxyModel.delayed = true;
|
||||
expressionFilter.w.count = 0;
|
||||
testFilterProxyModel.foo = 0;
|
||||
testFilterProxyModel.foo = 1;
|
||||
compare(testFilterProxyModel.count, 0);
|
||||
compare(expressionFilter.w.count, 0);
|
||||
wait(0);
|
||||
compare(testFilterProxyModel.count, 1);
|
||||
compare(expressionFilter.w.count, 1);
|
||||
}
|
||||
|
||||
function test_directSorters() {
|
||||
testSorterProxyModel.delayed = false;
|
||||
testSorterProxyModel.foo = true;
|
||||
compare(testSorterProxyModel.get(0).role1, 1);
|
||||
expressionSorter.w.count = 0;
|
||||
testSorterProxyModel.foo = false;
|
||||
compare(testSorterProxyModel.get(0).role1, 2);
|
||||
compare(expressionSorter.w.count, 6);
|
||||
wait(0);
|
||||
compare(testSorterProxyModel.get(0).role1, 2);
|
||||
compare(expressionSorter.w.count, 6);
|
||||
}
|
||||
|
||||
function test_delayedSorters() {
|
||||
testSorterProxyModel.delayed = false;
|
||||
testSorterProxyModel.foo = true;
|
||||
compare(testSorterProxyModel.get(0).role1, 1);
|
||||
testSorterProxyModel.delayed = true;
|
||||
expressionSorter.w.count = 0;
|
||||
testSorterProxyModel.foo = false;
|
||||
testSorterProxyModel.foo = true;
|
||||
testSorterProxyModel.foo = false;
|
||||
compare(testSorterProxyModel.get(0).role1, 1);
|
||||
compare(expressionSorter.w.count, 0);
|
||||
wait(0);
|
||||
compare(testSorterProxyModel.get(0).role1, 2);
|
||||
compare(expressionSorter.w.count, 2);
|
||||
}
|
||||
|
||||
function test_proxyRoles() {
|
||||
// init not delayed
|
||||
testRolesProxyModel.delayed = false;
|
||||
testRolesProxyModel.foo = true;
|
||||
compare(instantiator.object.foo, true);
|
||||
expressionRole.w.count = 0;
|
||||
dataChangedSpy.clear();
|
||||
|
||||
// test not delayed
|
||||
testRolesProxyModel.foo = false;
|
||||
compare(instantiator.object.foo, false);
|
||||
compare(dataChangedSpy.count, 3);
|
||||
var notDelayedCount = expressionRole.w.count; // why is it 12 and not just 3 ?
|
||||
wait(0);
|
||||
compare(instantiator.object.foo, false);
|
||||
compare(dataChangedSpy.count, 3);
|
||||
compare(expressionRole.w.count, notDelayedCount);
|
||||
|
||||
// init delayed
|
||||
testRolesProxyModel.delayed = true;
|
||||
expressionRole.w.count = 0;
|
||||
dataChangedSpy.clear();
|
||||
|
||||
// test delayed
|
||||
testRolesProxyModel.foo = true;
|
||||
testRolesProxyModel.foo = false;
|
||||
testRolesProxyModel.foo = true;
|
||||
compare(instantiator.object.foo, false);
|
||||
compare(dataChangedSpy.count, 0);
|
||||
compare(expressionRole.w.count, 0);
|
||||
wait(0);
|
||||
compare(instantiator.object.foo, true);
|
||||
compare(dataChangedSpy.count, 1);
|
||||
var expectedDelayedCount = notDelayedCount / 3;
|
||||
compare(expressionRole.w.count, expectedDelayedCount);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue