├── tests ├── BLACKLIST ├── tst_sortfilterproxymodel.cpp ├── indexsorter.h ├── indexsorter.cpp ├── SortFilterProxyModel.pro ├── tst_expressionrole.qml ├── testroles.h ├── tst_joinrole.qml ├── testroles.cpp ├── tst_sourceroles.qml ├── tst_filtersorter.qml ├── tst_filterrole.qml ├── tst_filtercontainerattached.qml ├── tst_builtins.qml ├── tst_regexprole.qml ├── tst_rolesorter.qml ├── tst_filtercontainers.qml ├── tst_stringsorter.qml ├── tst_switchrole.qml ├── tst_indexfilter.qml ├── tst_helpers.qml ├── tst_rangefilter.qml └── tst_proxyroles.qml ├── qvariantlessthan.h ├── sortfilterproxymodel.qdocconf ├── filters ├── alloffilter.h ├── anyoffilter.h ├── rolefilter.h ├── valuefilter.h ├── filtercontainerfilter.cpp ├── filtercontainerfilter.h ├── indexfilter.h ├── alloffilter.cpp ├── expressionfilter.h ├── regexpfilter.h ├── filter.h ├── filtersqmltypes.cpp ├── rolefilter.cpp ├── rangefilter.h ├── anyoffilter.cpp ├── valuefilter.cpp ├── filtercontainer.h ├── filter.cpp ├── regexpfilter.cpp ├── indexfilter.cpp ├── filtercontainer.cpp └── rangefilter.cpp ├── qpm.json ├── utils ├── utils.h └── utils.cpp ├── index.qdoc ├── docs ├── qml-sortfilterproxymodel-proxyrole-members.html ├── qml-sortfilterproxymodel-singlerole-members.html ├── qml-sortfilterproxymodel-filtercontainer-members.html ├── qml-sortfilterproxymodel-sortercontainer-members.html ├── qml-sortfilterproxymodel-allof-members.html ├── qml-sortfilterproxymodel-anyof-members.html ├── qml-sortfilterproxymodel-filter-members.html ├── qml-sortfilterproxymodel-filterrole-members.html ├── qml-sortfilterproxymodel-expressionrole-members.html ├── qml-sortfilterproxymodel-sorter-members.html ├── qml-sortfilterproxymodel-joinrole-members.html ├── qml-sortfilterproxymodel-rolefilter-members.html ├── qml-sortfilterproxymodel-regexprole-members.html ├── qml-sortfilterproxymodel-expressionfilter-members.html ├── qml-sortfilterproxymodel-rolesorter-members.html ├── qml-sortfilterproxymodel-valuefilter-members.html ├── qml-sortfilterproxymodel-indexfilter-members.html ├── qml-sortfilterproxymodel-filtersorter-members.html ├── qml-sortfilterproxymodel-expressionsorter-members.html ├── qml-sortfilterproxymodel-switchrole-members.html ├── qml-sortfilterproxymodel-regexpfilter-members.html ├── qml-sortfilterproxymodel-rangefilter-members.html ├── qml-sortfilterproxymodel-stringsorter-members.html ├── qml-sortfilterproxymodel-proxyrole.html ├── qml-sortfilterproxymodel-sortfilterproxymodel-members.html ├── qml-sortfilterproxymodel-singlerole.html ├── qml-sortfilterproxymodel-sortercontainer.html ├── qml-sortfilterproxymodel-allof.html ├── qml-sortfilterproxymodel-filter.html ├── qml-sortfilterproxymodel-filterrole.html ├── qml-sortfilterproxymodel-filtercontainer.html ├── qml-sortfilterproxymodel-anyof.html └── qml-sortfilterproxymodel-joinrole.html ├── proxyroles ├── filterrole.h ├── singlerole.h ├── proxyrolesqmltypes.cpp ├── joinrole.h ├── proxyrole.h ├── expressionrole.h ├── regexprole.h ├── proxyrole.cpp ├── singlerole.cpp ├── proxyrolecontainer.h ├── filterrole.cpp ├── switchrole.h ├── proxyrolecontainer.cpp ├── joinrole.cpp └── regexprole.cpp ├── sorters ├── rolesorter.h ├── sortersqmltypes.cpp ├── filtersorter.h ├── expressionsorter.h ├── stringsorter.h ├── sorter.h ├── sortercontainer.h ├── rolesorter.cpp ├── filtersorter.cpp ├── stringsorter.cpp ├── sorter.cpp └── sortercontainer.cpp ├── LICENSE ├── .gitignore ├── .github └── workflows │ ├── cmake-qt5.yml │ └── cmake-qt6.yml ├── CMakeLists.txt ├── SortFilterProxyModel.qbs ├── SortFilterProxyModel.pri └── qvariantlessthan.cpp /tests/BLACKLIST: -------------------------------------------------------------------------------- 1 | [StringSorterTests::test_stringSorters:doNotIgnorePunctuation] 2 | macos 3 | -------------------------------------------------------------------------------- /tests/tst_sortfilterproxymodel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | QUICK_TEST_MAIN(SortFilterProxyModel) 3 | -------------------------------------------------------------------------------- /qvariantlessthan.h: -------------------------------------------------------------------------------- 1 | #ifndef QVARIANTLESSTHAN_H 2 | #define QVARIANTLESSTHAN_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | bool lessThan(const QVariant &lhs, const QVariant &rhs); 9 | 10 | } // namespace qqsfpm 11 | 12 | #endif // QVARIANTLESSTHAN_H 13 | -------------------------------------------------------------------------------- /sortfilterproxymodel.qdocconf: -------------------------------------------------------------------------------- 1 | project = SortFilterProxyModel 2 | 3 | sourcedirs = . 4 | 5 | sources.fileextensions = "*.cpp *.qdoc *.qml" 6 | headers.fileextensions = "*.h" 7 | 8 | outputdir = docs/ 9 | 10 | HTML.templatedir = . 11 | HTML.stylesheets = "offline.css" 12 | 13 | HTML.headerstyles = " \n" 14 | -------------------------------------------------------------------------------- /filters/alloffilter.h: -------------------------------------------------------------------------------- 1 | #ifndef ALLOFFILTER_H 2 | #define ALLOFFILTER_H 3 | 4 | #include "filtercontainerfilter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class AllOfFilter : public FilterContainerFilter { 9 | Q_OBJECT 10 | 11 | public: 12 | using FilterContainerFilter::FilterContainerFilter; 13 | 14 | protected: 15 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 16 | }; 17 | 18 | } 19 | 20 | #endif // ALLOFFILTER_H 21 | -------------------------------------------------------------------------------- /filters/anyoffilter.h: -------------------------------------------------------------------------------- 1 | #ifndef ANYOFFILTER_H 2 | #define ANYOFFILTER_H 3 | 4 | #include "filtercontainerfilter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class AnyOfFilter : public FilterContainerFilter { 9 | Q_OBJECT 10 | 11 | public: 12 | using FilterContainerFilter::FilterContainerFilter; 13 | 14 | protected: 15 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 16 | }; 17 | 18 | } 19 | 20 | #endif // ANYOFFILTER_H 21 | -------------------------------------------------------------------------------- /qpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fr.grecko.sortfilterproxymodel", 3 | "description": "A nicely exposed QSortFilterProxyModel for QML", 4 | "author": { 5 | "name": "Pierre-Yves Siret", 6 | "email": "gr3cko@gmail.com" 7 | }, 8 | "repository": { 9 | "type": "GITHUB", 10 | "url": "https://github.com/oKcerG/SortFilterProxyModel.git" 11 | }, 12 | "version": { 13 | "label": "0.1.0", 14 | "revision": "", 15 | "fingerprint": "" 16 | }, 17 | "dependencies": [ 18 | ], 19 | "license": "MIT", 20 | "pri_filename": "SortFilterProxyModel.pri", 21 | "webpage": "" 22 | } -------------------------------------------------------------------------------- /utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | int compareVariants(const QVariant &lhs, const QVariant &rhs); 9 | 10 | inline bool operator<(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) < 0; } 11 | inline bool operator<=(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) <= 0; } 12 | inline bool operator>(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) > 0; } 13 | inline bool operator>=(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) >= 0; } 14 | 15 | } 16 | 17 | #endif // UTILS_H 18 | -------------------------------------------------------------------------------- /tests/indexsorter.h: -------------------------------------------------------------------------------- 1 | #ifndef INDEXSORTER_H 2 | #define INDEXSORTER_H 3 | 4 | #include "sorters/sorter.h" 5 | 6 | class IndexSorter : public qqsfpm::Sorter 7 | { 8 | public: 9 | using qqsfpm::Sorter::Sorter; 10 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) const override; 11 | }; 12 | 13 | class ReverseIndexSorter : public qqsfpm::Sorter 14 | { 15 | public: 16 | using qqsfpm::Sorter::Sorter; 17 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) const override; 18 | }; 19 | 20 | #endif // INDEXSORTER_H 21 | -------------------------------------------------------------------------------- /index.qdoc: -------------------------------------------------------------------------------- 1 | /*! 2 | \page index.html overview 3 | 4 | \title SortFilterProxyModel QML Module 5 | 6 | SortFilterProxyModel is an implementation of QSortFilterProxyModel conveniently exposed for QML. 7 | \annotatedlist SortFilterProxyModel 8 | 9 | \section1 Filters 10 | \annotatedlist Filters 11 | 12 | \section2 Related attached types 13 | \annotatedlist FilterAttached 14 | 15 | \section1 Sorters 16 | \annotatedlist Sorters 17 | 18 | \section2 Related attached types 19 | \annotatedlist SorterAttached 20 | 21 | \section1 ProxyRoles 22 | \annotatedlist ProxyRoles 23 | */ 24 | 25 | /*! 26 | \qmlmodule SortFilterProxyModel 27 | */ 28 | -------------------------------------------------------------------------------- /filters/rolefilter.h: -------------------------------------------------------------------------------- 1 | #ifndef ROLEFILTER_H 2 | #define ROLEFILTER_H 3 | 4 | #include "filter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class RoleFilter : public Filter 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged) 12 | 13 | public: 14 | using Filter::Filter; 15 | 16 | const QString& roleName() const; 17 | void setRoleName(const QString& roleName); 18 | 19 | Q_SIGNALS: 20 | void roleNameChanged(); 21 | 22 | protected: 23 | QVariant sourceData(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const; 24 | 25 | private: 26 | QString m_roleName; 27 | }; 28 | 29 | } 30 | 31 | #endif // ROLEFILTER_H 32 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-proxyrole-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for ProxyRole | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for ProxyRole

13 |

This is the complete list of members for ProxyRole, including inherited members.

14 | 15 | 16 | -------------------------------------------------------------------------------- /filters/valuefilter.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUEFILTER_H 2 | #define VALUEFILTER_H 3 | 4 | #include "rolefilter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class ValueFilter : public RoleFilter { 10 | Q_OBJECT 11 | Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) 12 | 13 | public: 14 | using RoleFilter::RoleFilter; 15 | 16 | const QVariant& value() const; 17 | void setValue(const QVariant& value); 18 | 19 | protected: 20 | bool filterRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 21 | 22 | Q_SIGNALS: 23 | void valueChanged(); 24 | 25 | private: 26 | QVariant m_value; 27 | }; 28 | 29 | } 30 | 31 | #endif // VALUEFILTER_H 32 | -------------------------------------------------------------------------------- /filters/filtercontainerfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "filtercontainerfilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | void FilterContainerFilter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 6 | { 7 | for (Filter* filter : m_filters) 8 | filter->proxyModelCompleted(proxyModel); 9 | } 10 | 11 | void FilterContainerFilter::onFilterAppended(Filter* filter) 12 | { 13 | connect(filter, &Filter::invalidated, this, &FilterContainerFilter::invalidate); 14 | invalidate(); 15 | } 16 | 17 | void FilterContainerFilter::onFilterRemoved(Filter* filter) 18 | { 19 | Q_UNUSED(filter) 20 | invalidate(); 21 | } 22 | 23 | void qqsfpm::FilterContainerFilter::onFiltersCleared() 24 | { 25 | invalidate(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /proxyroles/filterrole.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERROLE_H 2 | #define FILTERROLE_H 3 | 4 | #include "singlerole.h" 5 | #include "filters/filtercontainer.h" 6 | 7 | namespace qqsfpm { 8 | 9 | class FilterRole : public SingleRole, public FilterContainer 10 | { 11 | Q_OBJECT 12 | Q_INTERFACES(qqsfpm::FilterContainer) 13 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty) 14 | Q_CLASSINFO("DefaultProperty", "filters") 15 | 16 | public: 17 | using SingleRole::SingleRole; 18 | 19 | private: 20 | void onFilterAppended(Filter* filter) override; 21 | void onFilterRemoved(Filter* filter) override; 22 | void onFiltersCleared() override; 23 | 24 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 25 | }; 26 | 27 | } 28 | 29 | #endif // FILTERROLE_H 30 | -------------------------------------------------------------------------------- /proxyroles/singlerole.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLEROLE_H 2 | #define SINGLEROLE_H 3 | 4 | #include "proxyrole.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class SingleRole : public ProxyRole 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) 12 | 13 | public: 14 | using ProxyRole::ProxyRole; 15 | 16 | QString name() const; 17 | void setName(const QString& name); 18 | 19 | QStringList names() override; 20 | 21 | Q_SIGNALS: 22 | void nameChanged(); 23 | 24 | private: 25 | QString m_name; 26 | 27 | private: 28 | QVariant data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel, const QString &name) final; 29 | virtual QVariant data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel) = 0; 30 | }; 31 | 32 | } 33 | 34 | #endif // SINGLEROLE_H 35 | -------------------------------------------------------------------------------- /tests/indexsorter.cpp: -------------------------------------------------------------------------------- 1 | #include "indexsorter.h" 2 | #include 3 | 4 | int IndexSorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) const 5 | { 6 | Q_UNUSED(proxyModel) 7 | return sourceLeft.row() - sourceRight.row(); 8 | } 9 | 10 | int ReverseIndexSorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) const 11 | { 12 | Q_UNUSED(proxyModel) 13 | return sourceRight.row() - sourceLeft.row(); 14 | } 15 | 16 | void registerIndexSorterTypes() { 17 | qmlRegisterType("SortFilterProxyModel.Test", 0, 2, "IndexSorter"); 18 | qmlRegisterType("SortFilterProxyModel.Test", 0, 2, "ReverseIndexSorter"); 19 | } 20 | 21 | Q_COREAPP_STARTUP_FUNCTION(registerIndexSorterTypes) 22 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-singlerole-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for SingleRole | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for SingleRole

13 |

This is the complete list of members for SingleRole, including inherited members.

14 |
    15 |
  • name : string
  • 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /sorters/rolesorter.h: -------------------------------------------------------------------------------- 1 | #ifndef ROLESORTER_H 2 | #define ROLESORTER_H 3 | 4 | #include "sorter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class RoleSorter : public Sorter 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged) 12 | 13 | public: 14 | using Sorter::Sorter; 15 | 16 | const QString& roleName() const; 17 | void setRoleName(const QString& roleName); 18 | 19 | Q_SIGNALS: 20 | void roleNameChanged(); 21 | 22 | protected: 23 | QPair sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const; 24 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override; 25 | 26 | private: 27 | QString m_roleName; 28 | }; 29 | 30 | } 31 | 32 | #endif // ROLESORTER_H 33 | -------------------------------------------------------------------------------- /filters/filtercontainerfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERCONTAINERFILTER_H 2 | #define FILTERCONTAINERFILTER_H 3 | 4 | #include "filter.h" 5 | #include "filtercontainer.h" 6 | 7 | namespace qqsfpm { 8 | 9 | class FilterContainerFilter : public Filter, public FilterContainer { 10 | Q_OBJECT 11 | Q_INTERFACES(qqsfpm::FilterContainer) 12 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty NOTIFY filtersChanged) 13 | Q_CLASSINFO("DefaultProperty", "filters") 14 | 15 | public: 16 | using Filter::Filter; 17 | 18 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 19 | 20 | Q_SIGNALS: 21 | void filtersChanged(); 22 | 23 | private: 24 | void onFilterAppended(Filter* filter) override; 25 | void onFilterRemoved(Filter* filter) override; 26 | void onFiltersCleared() override; 27 | }; 28 | 29 | } 30 | 31 | #endif // FILTERCONTAINERFILTER_H 32 | -------------------------------------------------------------------------------- /proxyroles/proxyrolesqmltypes.cpp: -------------------------------------------------------------------------------- 1 | #include "proxyrole.h" 2 | #include "joinrole.h" 3 | #include "switchrole.h" 4 | #include "expressionrole.h" 5 | #include "regexprole.h" 6 | #include "filterrole.h" 7 | #include 8 | #include 9 | 10 | namespace qqsfpm { 11 | 12 | void registerProxyRoleTypes() { 13 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "ProxyRole", "ProxyRole is an abstract class"); 14 | qmlRegisterType("SortFilterProxyModel", 0, 2, "JoinRole"); 15 | qmlRegisterType("SortFilterProxyModel", 0, 2, "SwitchRole"); 16 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionRole"); 17 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpRole"); 18 | qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterRole"); 19 | } 20 | 21 | Q_COREAPP_STARTUP_FUNCTION(registerProxyRoleTypes) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-filtercontainer-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for FilterContainer | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for FilterContainer

13 |

This is the complete list of members for FilterContainer, including inherited members.

14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-sortercontainer-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for SorterContainer | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for SorterContainer

13 |

This is the complete list of members for SorterContainer, including inherited members.

14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-allof-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for AllOf | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for AllOf

13 |

This is the complete list of members for AllOf, including inherited members.

14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-anyof-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for AnyOf | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for AnyOf

13 |

This is the complete list of members for AnyOf, including inherited members.

14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-filter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for Filter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for Filter

13 |

This is the complete list of members for Filter, including inherited members.

14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /proxyroles/joinrole.h: -------------------------------------------------------------------------------- 1 | #ifndef JOINROLE_H 2 | #define JOINROLE_H 3 | 4 | #include "singlerole.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class JoinRole : public SingleRole 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QStringList roleNames READ roleNames WRITE setRoleNames NOTIFY roleNamesChanged) 12 | Q_PROPERTY(QString separator READ separator WRITE setSeparator NOTIFY separatorChanged) 13 | 14 | public: 15 | using SingleRole::SingleRole; 16 | 17 | QStringList roleNames() const; 18 | void setRoleNames(const QStringList& roleNames); 19 | 20 | QString separator() const; 21 | void setSeparator(const QString& separator); 22 | 23 | Q_SIGNALS: 24 | void roleNamesChanged(); 25 | 26 | void separatorChanged(); 27 | 28 | private: 29 | QStringList m_roleNames; 30 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 31 | QString m_separator = " "; 32 | }; 33 | 34 | } 35 | 36 | #endif // JOINROLE_H 37 | -------------------------------------------------------------------------------- /tests/SortFilterProxyModel.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = tst_sortfilterproxymodel 3 | QT += qml quick 4 | CONFIG += c++11 warn_on qmltestcase qml_debug no_keywords 5 | 6 | include(../SortFilterProxyModel.pri) 7 | 8 | HEADERS += \ 9 | indexsorter.h \ 10 | testroles.h 11 | 12 | SOURCES += \ 13 | tst_sortfilterproxymodel.cpp \ 14 | indexsorter.cpp \ 15 | testroles.cpp 16 | 17 | OTHER_FILES += \ 18 | tst_rangefilter.qml \ 19 | tst_indexfilter.qml \ 20 | tst_sourceroles.qml \ 21 | tst_sorters.qml \ 22 | tst_helpers.qml \ 23 | tst_builtins.qml \ 24 | tst_rolesorter.qml \ 25 | tst_stringsorter.qml \ 26 | tst_proxyroles.qml \ 27 | tst_joinrole.qml \ 28 | tst_switchrole.qml \ 29 | tst_expressionrole.qml \ 30 | tst_filtercontainerattached.qml \ 31 | tst_filtercontainers.qml \ 32 | tst_regexprole.qml \ 33 | tst_filtersorter.qml \ 34 | tst_filterrole.qml \ 35 | tst_delayed.qml \ 36 | tst_sortercontainerattached.qml 37 | -------------------------------------------------------------------------------- /proxyroles/proxyrole.h: -------------------------------------------------------------------------------- 1 | #ifndef PROXYROLE_H 2 | #define PROXYROLE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class QQmlSortFilterProxyModel; 10 | 11 | class ProxyRole : public QObject 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | using QObject::QObject; 17 | virtual ~ProxyRole() = default; 18 | 19 | QVariant roleData(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString& name); 20 | virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel); 21 | 22 | virtual QStringList names() = 0; 23 | 24 | protected: 25 | void invalidate(); 26 | 27 | Q_SIGNALS: 28 | void invalidated(); 29 | void namesAboutToBeChanged(); 30 | void namesChanged(); 31 | 32 | private: 33 | virtual QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString& name) = 0; 34 | 35 | QMutex m_mutex; 36 | }; 37 | 38 | } 39 | 40 | #endif // PROXYROLE_H 41 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-filterrole-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for FilterRole | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for FilterRole

13 |

This is the complete list of members for FilterRole, including inherited members.

14 |
    15 |
  • filters : list<Filter> [default]
  • 16 |
  • name : string
  • 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /sorters/sortersqmltypes.cpp: -------------------------------------------------------------------------------- 1 | #include "sorter.h" 2 | #include "rolesorter.h" 3 | #include "stringsorter.h" 4 | #include "filtersorter.h" 5 | #include "expressionsorter.h" 6 | #include "sortercontainer.h" 7 | #include 8 | #include 9 | 10 | namespace qqsfpm { 11 | 12 | void registerSorterTypes() { 13 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Sorter", "Sorter is an abstract class"); 14 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RoleSorter"); 15 | qmlRegisterType("SortFilterProxyModel", 0, 2, "StringSorter"); 16 | qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterSorter"); 17 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionSorter"); 18 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "SorterContainer", "SorterContainer can only be used as an attaching type"); 19 | } 20 | 21 | Q_COREAPP_STARTUP_FUNCTION(registerSorterTypes) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-expressionrole-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for ExpressionRole | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for ExpressionRole

13 |

This is the complete list of members for ExpressionRole, including inherited members.

14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /filters/indexfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef INDEXFILTER_H 2 | #define INDEXFILTER_H 3 | 4 | #include "filter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class IndexFilter: public Filter { 10 | Q_OBJECT 11 | Q_PROPERTY(QVariant minimumIndex READ minimumIndex WRITE setMinimumIndex NOTIFY minimumIndexChanged) 12 | Q_PROPERTY(QVariant maximumIndex READ maximumIndex WRITE setMaximumIndex NOTIFY maximumIndexChanged) 13 | 14 | public: 15 | using Filter::Filter; 16 | 17 | const QVariant& minimumIndex() const; 18 | void setMinimumIndex(const QVariant& minimumIndex); 19 | 20 | const QVariant& maximumIndex() const; 21 | void setMaximumIndex(const QVariant& maximumIndex); 22 | 23 | protected: 24 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 25 | 26 | Q_SIGNALS: 27 | void minimumIndexChanged(); 28 | void maximumIndexChanged(); 29 | 30 | private: 31 | QVariant m_minimumIndex; 32 | QVariant m_maximumIndex; 33 | }; 34 | 35 | } 36 | 37 | #endif // INDEXFILTER_H 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Pierre-Yves Siret 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /sorters/filtersorter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERSORTER_H 2 | #define FILTERSORTER_H 3 | 4 | #include "sorter.h" 5 | #include "filters/filtercontainer.h" 6 | 7 | namespace qqsfpm { 8 | 9 | class FilterSorter : public Sorter, public FilterContainer 10 | { 11 | Q_OBJECT 12 | Q_INTERFACES(qqsfpm::FilterContainer) 13 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty) 14 | Q_CLASSINFO("DefaultProperty", "filters") 15 | 16 | public: 17 | using Sorter::Sorter; 18 | 19 | protected: 20 | int compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel &proxyModel) const override; 21 | 22 | private: 23 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 24 | void onFilterAppended(Filter *filter) override; 25 | void onFilterRemoved(Filter *filter) override; 26 | void onFiltersCleared() override; 27 | 28 | bool indexIsAccepted(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel) const; 29 | }; 30 | 31 | } 32 | 33 | #endif // FILTERSORTER_H 34 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-sorter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for Sorter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for Sorter

13 |

This is the complete list of members for Sorter, including inherited members.

14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-joinrole-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for JoinRole | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for JoinRole

13 |

This is the complete list of members for JoinRole, including inherited members.

14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-rolefilter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for RoleFilter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for RoleFilter

13 |

This is the complete list of members for RoleFilter, including inherited members.

14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /filters/alloffilter.cpp: -------------------------------------------------------------------------------- 1 | #include "alloffilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype AllOf 7 | \inherits Filter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Filters 10 | \ingroup FilterContainer 11 | \brief Filter container accepting rows accepted by all its child filters. 12 | 13 | The AllOf type is a \l Filter container that accepts rows if all of its contained (and enabled) filters accept them, or if it has no filter. 14 | 15 | Using it as a top level filter has the same effect as putting all its child filters as top level filters. It can however be usefull to use an AllOf filter when nested in an AnyOf filter. 16 | \sa FilterContainer 17 | */ 18 | bool AllOfFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 19 | { 20 | //return true if all filters return false, or if there is no filter. 21 | return std::all_of(m_filters.begin(), m_filters.end(), 22 | [&sourceIndex, &proxyModel] (Filter* filter) { 23 | return filter->filterAcceptsRow(sourceIndex, proxyModel); 24 | } 25 | ); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /proxyroles/expressionrole.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPRESSIONROLE_H 2 | #define EXPRESSIONROLE_H 3 | 4 | #include "singlerole.h" 5 | #include 6 | 7 | class QQmlExpression; 8 | 9 | namespace qqsfpm { 10 | 11 | class ExpressionRole : public SingleRole 12 | { 13 | Q_OBJECT 14 | Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged) 15 | 16 | public: 17 | using SingleRole::SingleRole; 18 | 19 | const QQmlScriptString& expression() const; 20 | void setExpression(const QQmlScriptString& scriptString); 21 | 22 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 23 | 24 | Q_SIGNALS: 25 | void expressionChanged(); 26 | 27 | private: 28 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 29 | void updateContext(const QQmlSortFilterProxyModel& proxyModel); 30 | void updateExpression(); 31 | 32 | QQmlScriptString m_scriptString; 33 | QQmlExpression* m_expression = nullptr; 34 | QQmlContext* m_context = nullptr; 35 | }; 36 | 37 | } 38 | 39 | #endif // EXPRESSIONROLE_H 40 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-regexprole-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for RegExpRole | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for RegExpRole

13 |

This is the complete list of members for RegExpRole, including inherited members.

14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /filters/expressionfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPRESSIONFILTER_H 2 | #define EXPRESSIONFILTER_H 3 | 4 | #include "filter.h" 5 | #include 6 | 7 | class QQmlExpression; 8 | 9 | namespace qqsfpm { 10 | 11 | class ExpressionFilter : public Filter 12 | { 13 | Q_OBJECT 14 | Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged) 15 | 16 | public: 17 | using Filter::Filter; 18 | 19 | const QQmlScriptString& expression() const; 20 | void setExpression(const QQmlScriptString& scriptString); 21 | 22 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 23 | 24 | protected: 25 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 26 | 27 | Q_SIGNALS: 28 | void expressionChanged(); 29 | 30 | private: 31 | void updateContext(const QQmlSortFilterProxyModel& proxyModel); 32 | void updateExpression(); 33 | 34 | QQmlScriptString m_scriptString; 35 | QQmlExpression* m_expression = nullptr; 36 | QQmlContext* m_context = nullptr; 37 | }; 38 | 39 | } 40 | 41 | #endif // EXPRESSIONFILTER_H 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | .directory 25 | *.debug 26 | Makefile* 27 | *.prl 28 | *.app 29 | moc_*.cpp 30 | ui_*.h 31 | qrc_*.cpp 32 | *.qmlc 33 | Thumbs.db 34 | *.res 35 | *.rc 36 | /.qmake.cache 37 | /.qmake.stash 38 | 39 | # qtcreator generated files 40 | *.pro.user* 41 | 42 | # qtcreator shadow builds 43 | build-SortFilterProxyModel-* 44 | 45 | # xemacs temporary files 46 | *.flc 47 | 48 | # Vim temporary files 49 | .*.swp 50 | 51 | # Visual Studio generated files 52 | *.ib_pdb_index 53 | *.idb 54 | *.ilk 55 | *.pdb 56 | *.sln 57 | *.suo 58 | *.vcproj 59 | *vcproj.*.*.user 60 | *.ncb 61 | *.sdf 62 | *.opensdf 63 | *.vcxproj 64 | *vcxproj.* 65 | 66 | # MinGW generated files 67 | *.Debug 68 | *.Release 69 | 70 | # Python byte code 71 | *.pyc 72 | 73 | # Binaries 74 | # -------- 75 | *.dll 76 | *.exe 77 | 78 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-expressionfilter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for ExpressionFilter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for ExpressionFilter

13 |

This is the complete list of members for ExpressionFilter, including inherited members.

14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/tst_expressionrole.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import QtQml 2.2 6 | 7 | Item { 8 | property int c: 0 9 | ListModel { 10 | id: listModel 11 | ListElement { a: 1; b: 2 } 12 | } 13 | 14 | SortFilterProxyModel { 15 | id: testModel 16 | sourceModel: listModel 17 | 18 | proxyRoles: ExpressionRole { 19 | name: "expressionRole" 20 | expression: a + model.b + c 21 | } 22 | } 23 | 24 | Instantiator { 25 | id: instantiator 26 | model: testModel 27 | QtObject { 28 | property string expressionRole: model.expressionRole 29 | } 30 | } 31 | 32 | TestCase { 33 | name: "ExpressionRole" 34 | 35 | function test_expressionRole() { 36 | fuzzyCompare(instantiator.object.expressionRole, 3, 1e-7); 37 | listModel.setProperty(0, "b", 9); 38 | fuzzyCompare(instantiator.object.expressionRole, 10, 1e-7); 39 | c = 1327; 40 | fuzzyCompare(instantiator.object.expressionRole, 1337, 1e-7); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /filters/regexpfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef REGEXPFILTER_H 2 | #define REGEXPFILTER_H 3 | 4 | #include "rolefilter.h" 5 | 6 | #include 7 | 8 | namespace qqsfpm { 9 | 10 | class RegExpFilter : public RoleFilter { 11 | Q_OBJECT 12 | Q_PROPERTY(QString pattern READ pattern WRITE setPattern NOTIFY patternChanged) 13 | Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) 14 | 15 | public: 16 | using RoleFilter::RoleFilter; 17 | 18 | RegExpFilter(); 19 | 20 | QString pattern() const; 21 | void setPattern(const QString& pattern); 22 | 23 | Qt::CaseSensitivity caseSensitivity() const; 24 | void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); 25 | 26 | protected: 27 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 28 | 29 | Q_SIGNALS: 30 | void patternChanged(); 31 | void caseSensitivityChanged(); 32 | 33 | private: 34 | QRegularExpression m_regExp; 35 | Qt::CaseSensitivity m_caseSensitivity; 36 | QString m_pattern = m_regExp.pattern(); 37 | }; 38 | 39 | } 40 | 41 | #endif // REGEXPFILTER_H 42 | -------------------------------------------------------------------------------- /sorters/expressionsorter.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPRESSIONSORTER_H 2 | #define EXPRESSIONSORTER_H 3 | 4 | #include "sorter.h" 5 | #include 6 | 7 | class QQmlExpression; 8 | 9 | namespace qqsfpm { 10 | 11 | class QQmlSortFilterProxyModel; 12 | 13 | class ExpressionSorter : public Sorter 14 | { 15 | Q_OBJECT 16 | Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged) 17 | 18 | public: 19 | using Sorter::Sorter; 20 | 21 | const QQmlScriptString& expression() const; 22 | void setExpression(const QQmlScriptString& scriptString); 23 | 24 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 25 | 26 | Q_SIGNALS: 27 | void expressionChanged(); 28 | 29 | protected: 30 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override; 31 | 32 | private: 33 | void updateContext(const QQmlSortFilterProxyModel& proxyModel); 34 | void updateExpression(); 35 | 36 | QQmlScriptString m_scriptString; 37 | QQmlExpression* m_expression = nullptr; 38 | QQmlContext* m_context = nullptr; 39 | }; 40 | 41 | } 42 | 43 | #endif // EXPRESSIONSORTER_H 44 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-rolesorter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for RoleSorter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for RoleSorter

13 |

This is the complete list of members for RoleSorter, including inherited members.

14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-valuefilter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for ValueFilter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for ValueFilter

13 |

This is the complete list of members for ValueFilter, including inherited members.

14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-indexfilter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for IndexFilter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for IndexFilter

13 |

This is the complete list of members for IndexFilter, including inherited members.

14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /filters/filter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTER_H 2 | #define FILTER_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | class QQmlSortFilterProxyModel; 9 | 10 | class Filter : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) 14 | Q_PROPERTY(bool inverted READ inverted WRITE setInverted NOTIFY invertedChanged) 15 | 16 | public: 17 | explicit Filter(QObject *parent = nullptr); 18 | virtual ~Filter() = default; 19 | 20 | bool enabled() const; 21 | void setEnabled(bool enabled); 22 | 23 | bool inverted() const; 24 | void setInverted(bool inverted); 25 | 26 | bool filterAcceptsRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const; 27 | 28 | virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel); 29 | 30 | Q_SIGNALS: 31 | void enabledChanged(); 32 | void invertedChanged(); 33 | void invalidated(); 34 | 35 | protected: 36 | virtual bool filterRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const = 0; 37 | void invalidate(); 38 | 39 | private: 40 | bool m_enabled = true; 41 | bool m_inverted = false; 42 | }; 43 | 44 | } 45 | 46 | #endif // FILTER_H 47 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-filtersorter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for FilterSorter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for FilterSorter

13 |

This is the complete list of members for FilterSorter, including inherited members.

14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/testroles.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTROLES_H 2 | #define TESTROLES_H 3 | 4 | #include "proxyroles/singlerole.h" 5 | #include 6 | 7 | class StaticRole : public qqsfpm::SingleRole 8 | { 9 | Q_OBJECT 10 | Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) 11 | public: 12 | using qqsfpm::SingleRole::SingleRole; 13 | 14 | QVariant value() const; 15 | void setValue(const QVariant& value); 16 | 17 | Q_SIGNALS: 18 | void valueChanged(); 19 | 20 | protected: 21 | 22 | private: 23 | QVariant data(const QModelIndex& sourceIndex, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) override; 24 | QVariant m_value; 25 | }; 26 | 27 | class SourceIndexRole : public qqsfpm::SingleRole 28 | { 29 | public: 30 | using qqsfpm::SingleRole::SingleRole; 31 | 32 | private: 33 | QVariant data(const QModelIndex& sourceIndex, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) override; 34 | }; 35 | 36 | class MultiRole : public qqsfpm::ProxyRole 37 | { 38 | public: 39 | using qqsfpm::ProxyRole::ProxyRole; 40 | 41 | QStringList names() override; 42 | 43 | private: 44 | QVariant data(const QModelIndex &sourceIndex, const qqsfpm::QQmlSortFilterProxyModel &proxyModel, const QString &name) override; 45 | }; 46 | 47 | #endif // TESTROLES_H 48 | -------------------------------------------------------------------------------- /filters/filtersqmltypes.cpp: -------------------------------------------------------------------------------- 1 | #include "filter.h" 2 | #include "valuefilter.h" 3 | #include "indexfilter.h" 4 | #include "regexpfilter.h" 5 | #include "rangefilter.h" 6 | #include "expressionfilter.h" 7 | #include "anyoffilter.h" 8 | #include "alloffilter.h" 9 | #include 10 | #include 11 | 12 | namespace qqsfpm { 13 | 14 | void registerFiltersTypes() { 15 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Filter", "Filter is an abstract class"); 16 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ValueFilter"); 17 | qmlRegisterType("SortFilterProxyModel", 0, 2, "IndexFilter"); 18 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpFilter"); 19 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RangeFilter"); 20 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionFilter"); 21 | qmlRegisterType("SortFilterProxyModel", 0, 2, "AnyOf"); 22 | qmlRegisterType("SortFilterProxyModel", 0, 2, "AllOf"); 23 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "FilterContainer", "FilterContainer can only be used as an attaching type"); 24 | } 25 | 26 | Q_COREAPP_STARTUP_FUNCTION(registerFiltersTypes) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-expressionsorter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for ExpressionSorter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for ExpressionSorter

13 |

This is the complete list of members for ExpressionSorter, including inherited members.

14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /proxyroles/regexprole.h: -------------------------------------------------------------------------------- 1 | #ifndef REGEXPROLE_H 2 | #define REGEXPROLE_H 3 | 4 | #include "proxyrole.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class RegExpRole : public ProxyRole 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged) 13 | Q_PROPERTY(QString pattern READ pattern WRITE setPattern NOTIFY patternChanged) 14 | Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) 15 | 16 | public: 17 | using ProxyRole::ProxyRole; 18 | 19 | QString roleName() const; 20 | void setRoleName(const QString& roleName); 21 | 22 | QString pattern() const; 23 | void setPattern(const QString& pattern); 24 | 25 | Qt::CaseSensitivity caseSensitivity() const; 26 | void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); 27 | 28 | QStringList names() override; 29 | 30 | Q_SIGNALS: 31 | void roleNameChanged(); 32 | void patternChanged(); 33 | void caseSensitivityChanged(); 34 | 35 | private: 36 | QString m_roleName; 37 | QRegularExpression m_regularExpression; 38 | QVariant data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel, const QString &name) override; 39 | }; 40 | 41 | } 42 | 43 | #endif // REGEXPROLE_H 44 | -------------------------------------------------------------------------------- /tests/tst_joinrole.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import QtQml 2.2 6 | 7 | Item { 8 | ListModel { 9 | id: listModel 10 | ListElement { firstName: "Justin"; lastName: "Timberlake" } 11 | } 12 | 13 | SortFilterProxyModel { 14 | id: testModel 15 | sourceModel: listModel 16 | 17 | proxyRoles: JoinRole { 18 | id: joinRole 19 | name: "fullName" 20 | roleNames: ["firstName", "lastName"] 21 | } 22 | } 23 | 24 | Instantiator { 25 | id: instantiator 26 | model: testModel 27 | QtObject { 28 | property string fullName: model.fullName 29 | } 30 | } 31 | 32 | TestCase { 33 | name: "JoinRole" 34 | 35 | function test_joinRole() { 36 | compare(instantiator.object.fullName, "Justin Timberlake"); 37 | listModel.setProperty(0, "lastName", "Bieber"); 38 | compare(instantiator.object.fullName, "Justin Bieber"); 39 | joinRole.roleNames = ["lastName", "firstName"]; 40 | compare(instantiator.object.fullName, "Bieber Justin"); 41 | joinRole.separator = " - "; 42 | compare(instantiator.object.fullName, "Bieber - Justin"); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /filters/rolefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "rolefilter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype RoleFilter 8 | \qmlabstract 9 | \inherits Filter 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup Filters 12 | \brief Base type for filters based on a source model role. 13 | 14 | The RoleFilter type cannot be used directly in a QML file. 15 | It exists to provide a set of common properties and methods, 16 | available across all the other filter types that inherit from it. 17 | Attempting to use the RoleFilter type directly will result in an error. 18 | */ 19 | 20 | /*! 21 | \qmlproperty string RoleFilter::roleName 22 | 23 | This property holds the role name that the filter is using to query the source model's data when filtering items. 24 | */ 25 | const QString& RoleFilter::roleName() const 26 | { 27 | return m_roleName; 28 | } 29 | 30 | void RoleFilter::setRoleName(const QString& roleName) 31 | { 32 | if (m_roleName == roleName) 33 | return; 34 | 35 | m_roleName = roleName; 36 | Q_EMIT roleNameChanged(); 37 | invalidate(); 38 | } 39 | 40 | QVariant RoleFilter::sourceData(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 41 | { 42 | return proxyModel.sourceData(sourceIndex, m_roleName); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /proxyroles/proxyrole.cpp: -------------------------------------------------------------------------------- 1 | #include "proxyrole.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "filters/filter.h" 9 | #include "qqmlsortfilterproxymodel.h" 10 | 11 | namespace qqsfpm { 12 | 13 | /*! 14 | \qmltype ProxyRole 15 | \inqmlmodule SortFilterProxyModel 16 | \ingroup ProxyRoles 17 | \brief Base type for the \l SortFilterProxyModel proxy roles. 18 | 19 | The ProxyRole type cannot be used directly in a QML file. 20 | It exists to provide a set of common properties and methods, 21 | available across all the other proxy role types that inherit from it. 22 | Attempting to use the ProxyRole type directly will result in an error. 23 | */ 24 | 25 | QVariant ProxyRole::roleData(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString &name) 26 | { 27 | if (m_mutex.tryLock()) { 28 | QVariant result = data(sourceIndex, proxyModel, name); 29 | m_mutex.unlock(); 30 | return result; 31 | } else { 32 | return {}; 33 | } 34 | } 35 | 36 | void ProxyRole::proxyModelCompleted(const QQmlSortFilterProxyModel &proxyModel) 37 | { 38 | Q_UNUSED(proxyModel) 39 | } 40 | 41 | void ProxyRole::invalidate() 42 | { 43 | Q_EMIT invalidated(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /tests/testroles.cpp: -------------------------------------------------------------------------------- 1 | #include "testroles.h" 2 | #include 3 | 4 | QVariant StaticRole::value() const 5 | { 6 | return m_value; 7 | } 8 | 9 | void StaticRole::setValue(const QVariant& value) 10 | { 11 | if (m_value == value) 12 | return; 13 | 14 | m_value = value; 15 | Q_EMIT valueChanged(); 16 | invalidate(); 17 | } 18 | 19 | QVariant StaticRole::data(const QModelIndex& sourceIndex, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) 20 | { 21 | Q_UNUSED(sourceIndex) 22 | Q_UNUSED(proxyModel) 23 | return m_value; 24 | } 25 | 26 | QVariant SourceIndexRole::data(const QModelIndex& sourceIndex, const qqsfpm::QQmlSortFilterProxyModel& proxyModel) 27 | { 28 | Q_UNUSED(proxyModel) 29 | return sourceIndex.row(); 30 | } 31 | 32 | QStringList MultiRole::names() 33 | { 34 | return {"role1", "role2"}; 35 | } 36 | 37 | QVariant MultiRole::data(const QModelIndex&, const qqsfpm::QQmlSortFilterProxyModel&, const QString& name) 38 | { 39 | return "data for " + name; 40 | } 41 | 42 | void registerTestRolesTypes() { 43 | qmlRegisterType("SortFilterProxyModel.Test", 0, 2, "StaticRole"); 44 | qmlRegisterType("SortFilterProxyModel.Test", 0, 2, "SourceIndexRole"); 45 | qmlRegisterType("SortFilterProxyModel.Test", 0, 2, "MultiRole"); 46 | } 47 | 48 | Q_COREAPP_STARTUP_FUNCTION(registerTestRolesTypes) 49 | -------------------------------------------------------------------------------- /tests/tst_sourceroles.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.1 3 | import QtQml 2.2 4 | import SortFilterProxyModel 0.2 5 | 6 | Item { 7 | ListModel { 8 | id: nonEmptyFirstModel 9 | ListElement { 10 | test: "test" 11 | } 12 | } 13 | SortFilterProxyModel { 14 | id: nonEmptyFirstProxyModel 15 | sourceModel: nonEmptyFirstModel 16 | } 17 | Instantiator { 18 | id: nonEmptyFirstInstantiator 19 | model: nonEmptyFirstProxyModel 20 | QtObject { property var test: model.test } 21 | } 22 | 23 | ListModel { 24 | id: emptyFirstModel 25 | } 26 | SortFilterProxyModel { 27 | id: emptyFirstProxyModel 28 | sourceModel: emptyFirstModel 29 | } 30 | Instantiator { 31 | id: emptyFirstInstantiator 32 | model: emptyFirstProxyModel 33 | QtObject { property var test: model.test } 34 | } 35 | 36 | TestCase { 37 | name: "RoleTests" 38 | 39 | function test_nonEmptyFirst() { 40 | compare(nonEmptyFirstInstantiator.object.test, "test"); 41 | } 42 | 43 | function test_emptyFirst() { 44 | emptyFirstModel.append({test: "test"}); 45 | compare(emptyFirstProxyModel.get(0), {test: "test"}); 46 | compare(emptyFirstInstantiator.object.test, "test"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/tst_filtersorter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import QtQml 2.2 6 | 7 | Item { 8 | ListModel { 9 | id: listModel 10 | ListElement { name: "1"; favorite: true } 11 | ListElement { name: "2"; favorite: false } 12 | ListElement { name: "3"; favorite: false } 13 | ListElement { name: "4"; favorite: true } 14 | } 15 | 16 | SortFilterProxyModel { 17 | id: testModel 18 | sourceModel: listModel 19 | 20 | sorters: FilterSorter { 21 | ValueFilter { 22 | id: favoriteFilter 23 | roleName: "favorite" 24 | value: true 25 | } 26 | } 27 | } 28 | TestCase { 29 | name: "FilterSorter" 30 | 31 | function test_filterSorter() { 32 | compare(testModel.get(0, "name"), "1"); 33 | compare(testModel.get(1, "name"), "4"); 34 | compare(testModel.get(2, "name"), "2"); 35 | compare(testModel.get(3, "name"), "3"); 36 | 37 | favoriteFilter.value = false; 38 | 39 | compare(testModel.get(0, "name"), "2"); 40 | compare(testModel.get(1, "name"), "3"); 41 | compare(testModel.get(2, "name"), "1"); 42 | compare(testModel.get(3, "name"), "4"); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-switchrole-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for SwitchRole | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for SwitchRole

13 |

This is the complete list of members for SwitchRole, including inherited members.

14 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /proxyroles/singlerole.cpp: -------------------------------------------------------------------------------- 1 | #include "singlerole.h" 2 | #include 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype SingleRole 8 | \qmlabstract 9 | \inherits ProxyRole 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup ProxyRoles 12 | \brief Base type for the \l SortFilterProxyModel proxy roles defining a single role. 13 | 14 | SingleRole is a convenience base class for proxy roles who define a single role. 15 | It cannot be used directly in a QML file. 16 | It exists to provide a set of common properties and methods, 17 | available across all the other proxy role types that inherit from it. 18 | Attempting to use the SingleRole type directly will result in an error. 19 | */ 20 | /*! 21 | \qmlproperty string SingleRole::name 22 | 23 | This property holds the role name of the proxy role. 24 | */ 25 | QString SingleRole::name() const 26 | { 27 | return m_name; 28 | } 29 | 30 | void SingleRole::setName(const QString& name) 31 | { 32 | if (m_name == name) 33 | return; 34 | 35 | Q_EMIT namesAboutToBeChanged(); 36 | m_name = name; 37 | Q_EMIT nameChanged(); 38 | Q_EMIT namesChanged(); 39 | } 40 | 41 | QStringList SingleRole::names() 42 | { 43 | return QStringList { m_name }; 44 | } 45 | 46 | QVariant SingleRole::data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel, const QString &name) 47 | { 48 | Q_UNUSED(name); 49 | return data(sourceIndex, proxyModel); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /tests/tst_filterrole.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import QtQml 2.2 6 | 7 | Item { 8 | ListModel { 9 | id: listModel 10 | ListElement { name: "1"; age: 18 } 11 | ListElement { name: "2"; age: 22 } 12 | ListElement { name: "3"; age: 45 } 13 | ListElement { name: "4"; age: 10 } 14 | } 15 | 16 | SortFilterProxyModel { 17 | id: testModel 18 | sourceModel: listModel 19 | 20 | proxyRoles: FilterRole { 21 | name: "isOldEnough" 22 | RangeFilter { 23 | id: ageFilter 24 | roleName: "age" 25 | minimumInclusive: true 26 | minimumValue: 18 27 | } 28 | } 29 | } 30 | TestCase { 31 | name: "FilterRole" 32 | 33 | function test_filterRole() { 34 | compare(testModel.get(0, "isOldEnough"), true); 35 | compare(testModel.get(1, "isOldEnough"), true); 36 | compare(testModel.get(2, "isOldEnough"), true); 37 | compare(testModel.get(3, "isOldEnough"), false); 38 | 39 | ageFilter.minimumValue = 21; 40 | 41 | compare(testModel.get(0, "isOldEnough"), false); 42 | compare(testModel.get(1, "isOldEnough"), true); 43 | compare(testModel.get(2, "isOldEnough"), true); 44 | compare(testModel.get(3, "isOldEnough"), false); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-regexpfilter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for RegExpFilter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for RegExpFilter

13 |

This is the complete list of members for RegExpFilter, including inherited members.

14 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sorters/stringsorter.h: -------------------------------------------------------------------------------- 1 | #ifndef STRINGSORTER_H 2 | #define STRINGSORTER_H 3 | 4 | #include "rolesorter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class StringSorter : public RoleSorter 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) 13 | Q_PROPERTY(bool ignorePunctation READ ignorePunctation WRITE setIgnorePunctation NOTIFY ignorePunctationChanged) 14 | Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) 15 | Q_PROPERTY(bool numericMode READ numericMode WRITE setNumericMode NOTIFY numericModeChanged) 16 | 17 | public: 18 | using RoleSorter::RoleSorter; 19 | 20 | Qt::CaseSensitivity caseSensitivity() const; 21 | void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); 22 | 23 | bool ignorePunctation() const; 24 | void setIgnorePunctation(bool ignorePunctation); 25 | 26 | QLocale locale() const; 27 | void setLocale(const QLocale& locale); 28 | 29 | bool numericMode() const; 30 | void setNumericMode(bool numericMode); 31 | 32 | Q_SIGNALS: 33 | void caseSensitivityChanged(); 34 | void ignorePunctationChanged(); 35 | void localeChanged(); 36 | void numericModeChanged(); 37 | 38 | protected: 39 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override; 40 | 41 | private: 42 | QCollator m_collator; 43 | }; 44 | 45 | } 46 | 47 | #endif // STRINGSORTER_H 48 | -------------------------------------------------------------------------------- /proxyroles/proxyrolecontainer.h: -------------------------------------------------------------------------------- 1 | #ifndef PROXYROLECONTAINER_H 2 | #define PROXYROLECONTAINER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class ProxyRole; 10 | class QQmlSortFilterProxyModel; 11 | 12 | class ProxyRoleContainer { 13 | public: 14 | virtual ~ProxyRoleContainer() = default; 15 | 16 | QList proxyRoles() const; 17 | void appendProxyRole(ProxyRole* proxyRole); 18 | void removeProxyRole(ProxyRole* proxyRole); 19 | void clearProxyRoles(); 20 | 21 | QQmlListProperty proxyRolesListProperty(); 22 | 23 | protected: 24 | QList m_proxyRoles; 25 | 26 | private: 27 | virtual void onProxyRoleAppended(ProxyRole* proxyRole) = 0; 28 | virtual void onProxyRoleRemoved(ProxyRole* proxyRole) = 0; 29 | virtual void onProxyRolesCleared() = 0; 30 | 31 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 32 | using sizetype = int; 33 | #else 34 | using sizetype = qsizetype; 35 | #endif 36 | 37 | static void append_proxyRole(QQmlListProperty* list, ProxyRole* proxyRole); 38 | static sizetype count_proxyRole(QQmlListProperty* list); 39 | static ProxyRole* at_proxyRole(QQmlListProperty* list, sizetype index); 40 | static void clear_proxyRoles(QQmlListProperty* list); 41 | }; 42 | 43 | } 44 | 45 | #define ProxyRoleContainer_iid "fr.grecko.SortFilterProxyModel.ProxyRoleContainer" 46 | Q_DECLARE_INTERFACE(qqsfpm::ProxyRoleContainer, ProxyRoleContainer_iid) 47 | 48 | #endif // PROXYROLECONTAINER_H 49 | -------------------------------------------------------------------------------- /filters/rangefilter.h: -------------------------------------------------------------------------------- 1 | #ifndef RANGEFILTER_H 2 | #define RANGEFILTER_H 3 | 4 | #include "rolefilter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class RangeFilter : public RoleFilter 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(QVariant minimumValue READ minimumValue WRITE setMinimumValue NOTIFY minimumValueChanged) 13 | Q_PROPERTY(bool minimumInclusive READ minimumInclusive WRITE setMinimumInclusive NOTIFY minimumInclusiveChanged) 14 | Q_PROPERTY(QVariant maximumValue READ maximumValue WRITE setMaximumValue NOTIFY maximumValueChanged) 15 | Q_PROPERTY(bool maximumInclusive READ maximumInclusive WRITE setMaximumInclusive NOTIFY maximumInclusiveChanged) 16 | 17 | public: 18 | using RoleFilter::RoleFilter; 19 | 20 | QVariant minimumValue() const; 21 | void setMinimumValue(QVariant minimumValue); 22 | bool minimumInclusive() const; 23 | void setMinimumInclusive(bool minimumInclusive); 24 | 25 | QVariant maximumValue() const; 26 | void setMaximumValue(QVariant maximumValue); 27 | bool maximumInclusive() const; 28 | void setMaximumInclusive(bool maximumInclusive); 29 | 30 | protected: 31 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 32 | 33 | Q_SIGNALS: 34 | void minimumValueChanged(); 35 | void minimumInclusiveChanged(); 36 | void maximumValueChanged(); 37 | void maximumInclusiveChanged(); 38 | 39 | private: 40 | QVariant m_minimumValue; 41 | bool m_minimumInclusive = true; 42 | QVariant m_maximumValue; 43 | bool m_maximumInclusive = true; 44 | }; 45 | 46 | } 47 | 48 | #endif // RANGEFILTER_H 49 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-rangefilter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for RangeFilter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for RangeFilter

13 |

This is the complete list of members for RangeFilter, including inherited members.

14 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /filters/anyoffilter.cpp: -------------------------------------------------------------------------------- 1 | #include "anyoffilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype AnyOf 7 | \inherits Filter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Filters 10 | \ingroup FilterContainer 11 | \brief Filter container accepting rows accepted by at least one of its child filters. 12 | 13 | The AnyOf type is a \l Filter container that accepts rows if any of its contained (and enabled) filters accept them. 14 | 15 | In the following example, only the rows where the \c firstName role or the \c lastName role match the text entered in the \c nameTextField will be accepted : 16 | \code 17 | TextField { 18 | id: nameTextField 19 | } 20 | 21 | SortFilterProxyModel { 22 | sourceModel: contactModel 23 | filters: AnyOf { 24 | RegExpFilter { 25 | roleName: "lastName" 26 | pattern: nameTextField.text 27 | caseSensitivity: Qt.CaseInsensitive 28 | } 29 | RegExpFilter { 30 | roleName: "firstName" 31 | pattern: nameTextField.text 32 | caseSensitivity: Qt.CaseInsensitive 33 | } 34 | } 35 | } 36 | \endcode 37 | \sa FilterContainer 38 | */ 39 | bool AnyOfFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 40 | { 41 | //return true if any of the enabled filters return true 42 | return std::any_of(m_filters.begin(), m_filters.end(), 43 | [&sourceIndex, &proxyModel] (Filter* filter) { 44 | return filter->enabled() && filter->filterAcceptsRow(sourceIndex, proxyModel); 45 | } 46 | ); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /tests/tst_filtercontainerattached.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import SortFilterProxyModel 0.2 3 | import QtQml.Models 2.2 4 | import QtQml 2.2 5 | import QtTest 1.1 6 | 7 | Item { 8 | 9 | ListModel { 10 | id: dataModel 11 | ListElement { a: 0; b: 0; c: 0 } 12 | ListElement { a: 0; b: 0; c: 1 } 13 | ListElement { a: 0; b: 1; c: 0 } 14 | ListElement { a: 0; b: 1; c: 1 } 15 | ListElement { a: 1; b: 0; c: 0 } 16 | ListElement { a: 1; b: 0; c: 1 } 17 | ListElement { a: 1; b: 1; c: 0 } 18 | ListElement { a: 1; b: 1; c: 1 } 19 | } 20 | 21 | SortFilterProxyModel { 22 | id: testModel 23 | sourceModel: dataModel 24 | } 25 | 26 | Instantiator { 27 | id: filterInstantiator 28 | model: ["a", "b", "c"] 29 | delegate: ValueFilter { 30 | FilterContainer.container: testModel 31 | roleName: modelData 32 | value: 1 33 | } 34 | } 35 | 36 | TestCase { 37 | name: "FilterContainerAttached" 38 | 39 | function modelValues() { 40 | var modelValues = []; 41 | 42 | for (var i = 0; i < testModel.count; i++) 43 | modelValues.push(testModel.get(i)); 44 | 45 | return modelValues; 46 | } 47 | 48 | function test_filterContainers() { 49 | compare(filterInstantiator.count, 3); 50 | compare(modelValues(), [ { a: 1, b: 1, c: 1 }]); 51 | filterInstantiator.model = ["a", "b"]; 52 | wait(0); 53 | compare(filterInstantiator.count, 2) 54 | compare(modelValues(), [ { a: 1, b: 1, c: 0 }, { a: 1, b: 1, c: 1 }]); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/cmake-qt5.yml: -------------------------------------------------------------------------------- 1 | name: CMake-Qt5 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Install Qt 24 | uses: jurplel/install-qt-action@v3.2.1 25 | with: 26 | cache: 'true' 27 | cache-key-prefix: 'install-qt-action' 28 | version: '5.15.2' 29 | 30 | - name: Configure CMake 31 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 32 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 33 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 34 | 35 | - name: Build 36 | # Build your program with the given configuration 37 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 38 | 39 | - name: Test 40 | working-directory: ${{github.workspace}}/build 41 | # Execute tests defined by the CMake configuration. 42 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 43 | run: ctest -C ${{env.BUILD_TYPE}} 44 | 45 | -------------------------------------------------------------------------------- /.github/workflows/cmake-qt6.yml: -------------------------------------------------------------------------------- 1 | name: CMake-Qt6 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Install Qt 24 | uses: jurplel/install-qt-action@v3.2.1 25 | with: 26 | cache: 'true' 27 | cache-key-prefix: 'install-qt-action6' 28 | version: '6.5.0' 29 | 30 | - name: Configure CMake 31 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 32 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 33 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DQT_VERSION_MAJOR=6 34 | 35 | - name: Build 36 | # Build your program with the given configuration 37 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 38 | 39 | - name: Test 40 | working-directory: ${{github.workspace}}/build 41 | # Execute tests defined by the CMake configuration. 42 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 43 | run: ctest -C ${{env.BUILD_TYPE}} 44 | 45 | -------------------------------------------------------------------------------- /sorters/sorter.h: -------------------------------------------------------------------------------- 1 | #ifndef SORTER_H 2 | #define SORTER_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | class QQmlSortFilterProxyModel; 9 | 10 | class Sorter : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) 14 | Q_PROPERTY(bool ascendingOrder READ ascendingOrder WRITE setAscendingOrder NOTIFY sortOrderChanged) 15 | Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged) 16 | Q_PROPERTY(int priority READ priority WRITE setPriority NOTIFY priorityChanged) 17 | 18 | public: 19 | Sorter(QObject* parent = nullptr); 20 | virtual ~Sorter() = 0; 21 | 22 | bool enabled() const; 23 | void setEnabled(bool enabled); 24 | 25 | bool ascendingOrder() const; 26 | void setAscendingOrder(bool ascendingOrder); 27 | 28 | Qt::SortOrder sortOrder() const; 29 | void setSortOrder(Qt::SortOrder sortOrder); 30 | 31 | int priority() const; 32 | void setPriority(int priority); 33 | 34 | int compareRows(const QModelIndex& source_left, const QModelIndex& source_right, const QQmlSortFilterProxyModel& proxyModel) const; 35 | 36 | virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel); 37 | 38 | Q_SIGNALS: 39 | void enabledChanged(); 40 | void sortOrderChanged(); 41 | void priorityChanged(); 42 | 43 | void invalidated(); 44 | 45 | protected: 46 | virtual int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const; 47 | virtual bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const; 48 | void invalidate(); 49 | 50 | private: 51 | bool m_enabled = true; 52 | Qt::SortOrder m_sortOrder = Qt::AscendingOrder; 53 | int m_priority = 0; 54 | }; 55 | 56 | } 57 | 58 | #endif // SORTER_H 59 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-stringsorter-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for StringSorter | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for StringSorter

13 |

This is the complete list of members for StringSorter, including inherited members.

14 | 20 |

The following members are inherited from RoleSorter.

21 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-proxyrole.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ProxyRole QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 19 |

ProxyRole QML Type

20 | 21 | 22 |

Base type for the SortFilterProxyModel proxy roles. More...

23 | 24 |
25 |
Import Statement: import SortFilterProxyModel .
Inherited By:

RegExpRole and SingleRole

26 |
29 | 30 | 31 |

Detailed Description

32 |

The ProxyRole type cannot be used directly in a QML file. It exists to provide a set of common properties and methods, available across all the other proxy role types that inherit from it. Attempting to use the ProxyRole type directly will result in an error.

33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/tst_builtins.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | 6 | Item { 7 | ListModel { 8 | id: testModel 9 | ListElement{role1: 1; role2: 1} 10 | ListElement{role1: 2; role2: 1} 11 | ListElement{role1: 3; role2: 2} 12 | ListElement{role1: 4; role2: 2} 13 | } 14 | ListModel { 15 | id: noRolesFirstTestModel 16 | function initModel() { 17 | noRolesFirstTestModel.append({role1: 1, role2: 1 }) 18 | noRolesFirstTestModel.append({role1: 2, role2: 1 }) 19 | noRolesFirstTestModel.append({role1: 3, role2: 2 }) 20 | noRolesFirstTestModel.append({role1: 4, role2: 2 }) 21 | } 22 | } 23 | SortFilterProxyModel { 24 | id: testProxyModel 25 | property string tag: "testProxyModel" 26 | sourceModel: testModel 27 | filterRoleName: "role2" 28 | filterValue: 2 29 | property var expectedData: ([{role1: 3, role2: 2}, {role1: 4, role2: 2}]) 30 | } 31 | SortFilterProxyModel { 32 | id: noRolesFirstTestProxyModel 33 | property string tag: "noRolesFirstTestProxyModel" 34 | sourceModel: noRolesFirstTestModel 35 | filterRoleName: "role2" 36 | filterValue: 2 37 | property var expectedData: ([{role1: 3, role2: 2}, {role1: 4, role2: 2}]) 38 | } 39 | TestCase { 40 | name: "BuiltinsFilterTests" 41 | function test_filterValue_data() { 42 | return [testProxyModel, noRolesFirstTestProxyModel]; 43 | } 44 | 45 | function test_filterValue(proxyModel) { 46 | if (proxyModel.sourceModel.initModel) 47 | proxyModel.sourceModel.initModel() 48 | var data = []; 49 | for (var i = 0; i < proxyModel.count; i++) 50 | data.push(proxyModel.get(i)); 51 | compare(data, proxyModel.expectedData); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /proxyroles/filterrole.cpp: -------------------------------------------------------------------------------- 1 | #include "filterrole.h" 2 | #include "filters/filter.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype FilterRole 8 | \inherits SingleRole 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup ProxyRoles 11 | \ingroup FilterContainer 12 | \brief A role resolving to \c true for rows matching all its filters. 13 | 14 | A FilterRole is a \l ProxyRole that returns \c true for rows matching all its filters. 15 | 16 | In the following example, the \c isAdult role will be equal to \c true if the \c age role is superior or equal to 18. 17 | \code 18 | SortFilterProxyModel { 19 | sourceModel: personModel 20 | proxyRoles: FilterRole { 21 | name: "isAdult" 22 | RangeFilter { roleName: "age"; minimumValue: 18; minimumInclusive: true } 23 | } 24 | } 25 | \endcode 26 | \sa FilterContainer 27 | */ 28 | 29 | /*! 30 | \qmlproperty list FilterRole::filters 31 | \default 32 | 33 | This property holds the list of filters for this filter role. 34 | The data of this role will be equal to the \c true if all its filters match the model row, \c false otherwise. 35 | 36 | \sa Filter, FilterContainer 37 | */ 38 | 39 | void FilterRole::onFilterAppended(Filter* filter) 40 | { 41 | connect(filter, &Filter::invalidated, this, &FilterRole::invalidate); 42 | invalidate(); 43 | } 44 | 45 | void FilterRole::onFilterRemoved(Filter* filter) 46 | { 47 | disconnect(filter, &Filter::invalidated, this, &FilterRole::invalidate); 48 | invalidate(); 49 | } 50 | 51 | void FilterRole::onFiltersCleared() 52 | { 53 | invalidate(); 54 | } 55 | 56 | QVariant FilterRole::data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) 57 | { 58 | return std::all_of(m_filters.begin(), m_filters.end(), 59 | [&] (Filter* filter) { 60 | return filter->filterAcceptsRow(sourceIndex, proxyModel); 61 | } 62 | ); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | if (NOT QT_VERSION_MAJOR) 6 | set(QT_VERSION_MAJOR 5) 7 | endif() 8 | 9 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS 10 | Core 11 | Qml 12 | ) 13 | 14 | set(CMAKE_AUTOMOC ON) 15 | set(CMAKE_INCLUDE_CURRENT_DIR ON) # This is to find generated *.moc and *.h files in build dir 16 | 17 | add_library(SortFilterProxyModel OBJECT 18 | qqmlsortfilterproxymodel.cpp 19 | qvariantlessthan.cpp 20 | filters/filter.cpp 21 | filters/filtercontainer.cpp 22 | filters/rolefilter.cpp 23 | filters/valuefilter.cpp 24 | filters/indexfilter.cpp 25 | filters/regexpfilter.cpp 26 | filters/rangefilter.cpp 27 | filters/expressionfilter.cpp 28 | filters/filtercontainerfilter.cpp 29 | filters/anyoffilter.cpp 30 | filters/alloffilter.cpp 31 | filters/filtersqmltypes.cpp 32 | sorters/sorter.cpp 33 | sorters/sortercontainer.cpp 34 | sorters/rolesorter.cpp 35 | sorters/stringsorter.cpp 36 | sorters/expressionsorter.cpp 37 | sorters/sortersqmltypes.cpp 38 | proxyroles/proxyrole.cpp 39 | proxyroles/proxyrolecontainer.cpp 40 | proxyroles/joinrole.cpp 41 | proxyroles/switchrole.cpp 42 | proxyroles/expressionrole.cpp 43 | proxyroles/proxyrolesqmltypes.cpp 44 | proxyroles/singlerole.cpp 45 | proxyroles/regexprole.cpp 46 | sorters/filtersorter.cpp 47 | proxyroles/filterrole.cpp 48 | utils/utils.cpp 49 | utils/utils.h 50 | ) 51 | 52 | target_link_libraries(SortFilterProxyModel 53 | PRIVATE 54 | Qt::Core 55 | Qt::Qml 56 | ) 57 | 58 | if ((MSVC) AND (MSVC_VERSION GREATER_EQUAL 1914)) 59 | target_compile_options(SortFilterProxyModel PUBLIC "/Zc:__cplusplus") 60 | endif() 61 | 62 | target_include_directories(SortFilterProxyModel PUBLIC 63 | ${CMAKE_CURRENT_LIST_DIR} 64 | $ 65 | $ 66 | ) 67 | -------------------------------------------------------------------------------- /filters/valuefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "valuefilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype ValueFilter 7 | \inherits RoleFilter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Filters 10 | \brief Filters rows matching exactly a value. 11 | 12 | A ValueFilter is a simple \l RoleFilter that accepts rows matching exactly the filter's value 13 | 14 | In the following example, only rows with their \c favorite role set to \c true will be accepted when the checkbox is checked : 15 | \code 16 | CheckBox { 17 | id: showOnlyFavoriteCheckBox 18 | } 19 | 20 | SortFilterProxyModel { 21 | sourceModel: contactModel 22 | filters: ValueFilter { 23 | roleName: "favorite" 24 | value: true 25 | enabled: showOnlyFavoriteCheckBox.checked 26 | } 27 | } 28 | \endcode 29 | 30 | */ 31 | 32 | /*! 33 | \qmlproperty variant ValueFilter::value 34 | 35 | This property holds the value used to filter the contents of the source model. 36 | */ 37 | const QVariant &ValueFilter::value() const 38 | { 39 | return m_value; 40 | } 41 | 42 | void ValueFilter::setValue(const QVariant& value) 43 | { 44 | if (m_value == value) 45 | return; 46 | 47 | m_value = value; 48 | Q_EMIT valueChanged(); 49 | invalidate(); 50 | } 51 | 52 | bool ValueFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 53 | { 54 | QVariant srcData = sourceData(sourceIndex, proxyModel); 55 | #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) 56 | // Implicitly convert the types. This was the behavior in Qt5 and makes QML 57 | // interop much easier, e.g. when comparing QByteArray against QString 58 | if (srcData.metaType() != m_value.metaType()) { 59 | QVariant converted = srcData; 60 | if (converted.convert(m_value.metaType())) { 61 | srcData = converted; 62 | } 63 | } 64 | #endif 65 | return !m_value.isValid() || m_value == srcData; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /tests/tst_regexprole.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import QtQml 2.2 6 | 7 | Item { 8 | ListModel { 9 | id: listModel 10 | ListElement { dummyRole: false; compoundRole: "0 - zero"; unusedRole: "" } 11 | ListElement { dummyRole: false; compoundRole: "1 - one"; unusedRole: "" } 12 | ListElement { dummyRole: false; compoundRole: "2 - two"; unusedRole: "" } 13 | ListElement { dummyRole: false; compoundRole: "3 - three"; unusedRole: "" } 14 | ListElement { dummyRole: false; compoundRole: "four"; unusedRole: "" } 15 | } 16 | 17 | SortFilterProxyModel { 18 | id: testModel 19 | sourceModel: listModel 20 | 21 | proxyRoles: [ 22 | RegExpRole { 23 | id: regExpRole 24 | roleName: "compoundRole" 25 | pattern: "(?\\d+) - (?.+)" 26 | }, 27 | RegExpRole { 28 | id: caseSensitiveRole 29 | roleName: "compoundRole" 30 | pattern: "\\d+ - (?[A-Z]+)" 31 | caseSensitivity: Qt.CaseSensitive 32 | }, 33 | RegExpRole { 34 | id: caseInsensitiveRole 35 | roleName: "compoundRole" 36 | pattern: "\\d+ - (?[A-Z]+)" 37 | caseSensitivity: Qt.CaseInsensitive 38 | } 39 | ] 40 | } 41 | 42 | TestCase { 43 | name: "RegExpRole" 44 | 45 | function test_regExpRole() { 46 | compare(testModel.get(0, "id"), "0"); 47 | compare(testModel.get(1, "id"), "1"); 48 | compare(testModel.get(0, "name"), "zero"); 49 | compare(testModel.get(4, "id"), undefined); 50 | compare(testModel.get(0, "nameCS"), undefined); 51 | compare(testModel.get(0, "nameCIS"), "zero"); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /proxyroles/switchrole.h: -------------------------------------------------------------------------------- 1 | #ifndef SWITCHROLE_H 2 | #define SWITCHROLE_H 3 | 4 | #include "singlerole.h" 5 | #include "filters/filtercontainer.h" 6 | #include 7 | 8 | namespace qqsfpm { 9 | 10 | class SwitchRoleAttached : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) 14 | public: 15 | SwitchRoleAttached(QObject* parent); 16 | 17 | QVariant value() const; 18 | void setValue(QVariant value); 19 | 20 | Q_SIGNALS: 21 | void valueChanged(); 22 | 23 | private: 24 | QVariant m_value; 25 | }; 26 | 27 | class SwitchRole : public SingleRole, public FilterContainer 28 | { 29 | Q_OBJECT 30 | Q_INTERFACES(qqsfpm::FilterContainer) 31 | Q_PROPERTY(QString defaultRoleName READ defaultRoleName WRITE setDefaultRoleName NOTIFY defaultRoleNameChanged) 32 | Q_PROPERTY(QVariant defaultValue READ defaultValue WRITE setDefaultValue NOTIFY defaultValueChanged) 33 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty) 34 | Q_CLASSINFO("DefaultProperty", "filters") 35 | 36 | public: 37 | using SingleRole::SingleRole; 38 | 39 | QString defaultRoleName() const; 40 | void setDefaultRoleName(const QString& defaultRoleName); 41 | 42 | QVariant defaultValue() const; 43 | void setDefaultValue(const QVariant& defaultValue); 44 | 45 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 46 | 47 | static SwitchRoleAttached* qmlAttachedProperties(QObject* object); 48 | 49 | Q_SIGNALS: 50 | void defaultRoleNameChanged(); 51 | void defaultValueChanged(); 52 | 53 | private: 54 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 55 | 56 | void onFilterAppended(Filter *filter) override; 57 | void onFilterRemoved(Filter *filter) override; 58 | void onFiltersCleared() override; 59 | 60 | QString m_defaultRoleName; 61 | QVariant m_defaultValue; 62 | }; 63 | 64 | } 65 | 66 | QML_DECLARE_TYPEINFO(qqsfpm::SwitchRole, QML_HAS_ATTACHED_PROPERTIES) 67 | 68 | #endif // SWITCHROLE_H 69 | -------------------------------------------------------------------------------- /filters/filtercontainer.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERCONTAINER_H 2 | #define FILTERCONTAINER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace qqsfpm { 10 | 11 | class Filter; 12 | class QQmlSortFilterProxyModel; 13 | 14 | class FilterContainer { 15 | public: 16 | virtual ~FilterContainer() = default; 17 | 18 | QList filters() const; 19 | void appendFilter(Filter* filter); 20 | void removeFilter(Filter* filter); 21 | void clearFilters(); 22 | 23 | QQmlListProperty filtersListProperty(); 24 | 25 | protected: 26 | QList m_filters; 27 | 28 | private: 29 | virtual void onFilterAppended(Filter* filter) = 0; 30 | virtual void onFilterRemoved(Filter* filter) = 0; 31 | virtual void onFiltersCleared() = 0; 32 | 33 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 34 | using sizetype = int; 35 | #else 36 | using sizetype = qsizetype; 37 | #endif 38 | 39 | static void append_filter(QQmlListProperty* list, Filter* filter); 40 | static sizetype count_filter(QQmlListProperty* list); 41 | static Filter* at_filter(QQmlListProperty* list, sizetype index); 42 | static void clear_filters(QQmlListProperty* list); 43 | }; 44 | 45 | class FilterContainerAttached : public QObject 46 | { 47 | Q_OBJECT 48 | Q_PROPERTY(QObject* container READ container WRITE setContainer NOTIFY containerChanged) 49 | 50 | public: 51 | FilterContainerAttached(QObject* object); 52 | ~FilterContainerAttached(); 53 | 54 | QObject* container() const; 55 | void setContainer(QObject* object); 56 | 57 | static FilterContainerAttached* qmlAttachedProperties(QObject* object); 58 | 59 | Q_SIGNALS: 60 | void containerChanged(); 61 | 62 | private: 63 | QPointer m_container = nullptr; 64 | Filter* m_filter = nullptr; 65 | }; 66 | 67 | } 68 | 69 | #define FilterContainer_iid "fr.grecko.SortFilterProxyModel.FilterContainer" 70 | Q_DECLARE_INTERFACE(qqsfpm::FilterContainer, FilterContainer_iid) 71 | 72 | QML_DECLARE_TYPEINFO(qqsfpm::FilterContainerAttached, QML_HAS_ATTACHED_PROPERTIES) 73 | 74 | #endif // FILTERCONTAINER_H 75 | -------------------------------------------------------------------------------- /proxyroles/proxyrolecontainer.cpp: -------------------------------------------------------------------------------- 1 | #include "proxyrolecontainer.h" 2 | 3 | namespace qqsfpm { 4 | 5 | QList ProxyRoleContainer::proxyRoles() const 6 | { 7 | return m_proxyRoles; 8 | } 9 | 10 | void ProxyRoleContainer::appendProxyRole(ProxyRole* proxyRole) 11 | { 12 | m_proxyRoles.append(proxyRole); 13 | onProxyRoleAppended(proxyRole); 14 | } 15 | 16 | void ProxyRoleContainer::removeProxyRole(ProxyRole* proxyRole) 17 | { 18 | m_proxyRoles.removeOne(proxyRole); 19 | onProxyRoleRemoved(proxyRole); 20 | } 21 | 22 | void ProxyRoleContainer::clearProxyRoles() 23 | { 24 | m_proxyRoles.clear(); 25 | onProxyRolesCleared(); 26 | } 27 | 28 | QQmlListProperty ProxyRoleContainer::proxyRolesListProperty() 29 | { 30 | return QQmlListProperty(reinterpret_cast(this), &m_proxyRoles, 31 | &ProxyRoleContainer::append_proxyRole, 32 | &ProxyRoleContainer::count_proxyRole, 33 | &ProxyRoleContainer::at_proxyRole, 34 | &ProxyRoleContainer::clear_proxyRoles); 35 | } 36 | 37 | void ProxyRoleContainer::append_proxyRole(QQmlListProperty* list, ProxyRole* proxyRole) 38 | { 39 | if (!proxyRole) 40 | return; 41 | 42 | ProxyRoleContainer* that = reinterpret_cast(list->object); 43 | that->appendProxyRole(proxyRole); 44 | } 45 | 46 | qqsfpm::ProxyRoleContainer::sizetype ProxyRoleContainer::count_proxyRole(QQmlListProperty* list) 47 | { 48 | QList* ProxyRoles = static_cast*>(list->data); 49 | return ProxyRoles->count(); 50 | } 51 | 52 | ProxyRole* ProxyRoleContainer::at_proxyRole(QQmlListProperty* list, qqsfpm::ProxyRoleContainer::sizetype index) 53 | { 54 | QList* ProxyRoles = static_cast*>(list->data); 55 | return ProxyRoles->at(index); 56 | } 57 | 58 | void ProxyRoleContainer::clear_proxyRoles(QQmlListProperty *list) 59 | { 60 | ProxyRoleContainer* that = reinterpret_cast(list->object); 61 | that->clearProxyRoles(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /sorters/sortercontainer.h: -------------------------------------------------------------------------------- 1 | #ifndef SORTERSSORTERCONTAINER_H 2 | #define SORTERSSORTERCONTAINER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace qqsfpm { 10 | 11 | class Sorter; 12 | class QQmlSortFilterProxyModel; 13 | 14 | class SorterContainer { 15 | public: 16 | virtual ~SorterContainer() = default; 17 | 18 | QList sorters() const; 19 | void appendSorter(Sorter* sorter); 20 | void removeSorter(Sorter* sorter); 21 | void clearSorters(); 22 | 23 | QQmlListProperty sortersListProperty(); 24 | 25 | protected: 26 | QList m_sorters; 27 | 28 | private: 29 | virtual void onSorterAppended(Sorter* sorter) = 0; 30 | virtual void onSorterRemoved(Sorter* sorter) = 0; 31 | virtual void onSortersCleared() = 0; 32 | 33 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 34 | using sizetype = int; 35 | #else 36 | using sizetype = qsizetype; 37 | #endif 38 | 39 | static void append_sorter(QQmlListProperty* list, Sorter* sorter); 40 | static sizetype count_sorter(QQmlListProperty* list); 41 | static Sorter* at_sorter(QQmlListProperty* list, sizetype index); 42 | static void clear_sorters(QQmlListProperty* list); 43 | }; 44 | 45 | class SorterContainerAttached : public QObject 46 | { 47 | Q_OBJECT 48 | Q_PROPERTY(QObject* container READ container WRITE setContainer NOTIFY containerChanged) 49 | 50 | public: 51 | SorterContainerAttached(QObject* object); 52 | ~SorterContainerAttached(); 53 | 54 | QObject* container() const; 55 | void setContainer(QObject* object); 56 | 57 | static SorterContainerAttached* qmlAttachedProperties(QObject* object); 58 | 59 | Q_SIGNALS: 60 | void containerChanged(); 61 | 62 | private: 63 | QPointer m_container = nullptr; 64 | Sorter* m_sorter = nullptr; 65 | }; 66 | 67 | } 68 | 69 | #define SorterContainer_iid "fr.grecko.SortFilterProxyModel.SorterContainer" 70 | Q_DECLARE_INTERFACE(qqsfpm::SorterContainer, SorterContainer_iid) 71 | 72 | QML_DECLARE_TYPEINFO(qqsfpm::SorterContainerAttached, QML_HAS_ATTACHED_PROPERTIES) 73 | 74 | #endif // SORTERSSORTERCONTAINER_H 75 | -------------------------------------------------------------------------------- /sorters/rolesorter.cpp: -------------------------------------------------------------------------------- 1 | #include "rolesorter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | #include "qvariantlessthan.h" 5 | 6 | 7 | 8 | 9 | namespace qqsfpm { 10 | 11 | /*! 12 | \qmltype RoleSorter 13 | \inherits Sorter 14 | \inqmlmodule SortFilterProxyModel 15 | \ingroup Sorters 16 | \brief Sorts rows based on a source model role. 17 | 18 | A RoleSorter is a simple \l Sorter that sorts rows based on a source model role. 19 | 20 | In the following example, rows with be sorted by their \c lastName role : 21 | \code 22 | SortFilterProxyModel { 23 | sourceModel: contactModel 24 | sorters: RoleSorter { roleName: "lastName" } 25 | } 26 | \endcode 27 | */ 28 | 29 | /*! 30 | \qmlproperty string RoleSorter::roleName 31 | 32 | This property holds the role name that the sorter is using to query the source model's data when sorting items. 33 | */ 34 | const QString& RoleSorter::roleName() const 35 | { 36 | return m_roleName; 37 | } 38 | 39 | void RoleSorter::setRoleName(const QString& roleName) 40 | { 41 | if (m_roleName == roleName) 42 | return; 43 | 44 | m_roleName = roleName; 45 | Q_EMIT roleNameChanged(); 46 | invalidate(); 47 | } 48 | 49 | QPair RoleSorter::sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 50 | { 51 | QPair pair; 52 | int role = proxyModel.roleForName(m_roleName); 53 | 54 | if (role == -1) 55 | return pair; 56 | 57 | pair.first = proxyModel.sourceData(sourceLeft, role); 58 | pair.second = proxyModel.sourceData(sourceRight, role); 59 | return pair; 60 | } 61 | 62 | int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 63 | { 64 | 65 | QPair pair = sourceData(sourceLeft, sourceRight, proxyModel); 66 | QVariant leftValue = pair.first; 67 | QVariant rightValue = pair.second; 68 | if (qqsfpm::lessThan(leftValue, rightValue)) 69 | return -1; 70 | if (qqsfpm::lessThan(rightValue, leftValue)) 71 | return 1; 72 | return 0; 73 | 74 | } 75 | 76 | 77 | } -------------------------------------------------------------------------------- /filters/filter.cpp: -------------------------------------------------------------------------------- 1 | #include "filter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype Filter 8 | \qmlabstract 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Filters 11 | \brief Base type for the \l SortFilterProxyModel filters. 12 | 13 | The Filter type cannot be used directly in a QML file. 14 | It exists to provide a set of common properties and methods, 15 | available across all the other filter types that inherit from it. 16 | Attempting to use the Filter type directly will result in an error. 17 | */ 18 | 19 | Filter::Filter(QObject *parent) : QObject(parent) 20 | { 21 | } 22 | 23 | /*! 24 | \qmlproperty bool Filter::enabled 25 | 26 | This property holds whether the filter is enabled. 27 | A disabled filter will accept every rows unconditionally (even if it's inverted). 28 | 29 | By default, filters are enabled. 30 | */ 31 | bool Filter::enabled() const 32 | { 33 | return m_enabled; 34 | } 35 | 36 | void Filter::setEnabled(bool enabled) 37 | { 38 | if (m_enabled == enabled) 39 | return; 40 | 41 | m_enabled = enabled; 42 | Q_EMIT enabledChanged(); 43 | Q_EMIT invalidated(); 44 | } 45 | 46 | /*! 47 | \qmlproperty bool Filter::inverted 48 | 49 | This property holds whether the filter is inverted. 50 | When a filter is inverted, a row normally accepted would be rejected, and vice-versa. 51 | 52 | By default, filters are not inverted. 53 | */ 54 | bool Filter::inverted() const 55 | { 56 | return m_inverted; 57 | } 58 | 59 | void Filter::setInverted(bool inverted) 60 | { 61 | if (m_inverted == inverted) 62 | return; 63 | 64 | m_inverted = inverted; 65 | Q_EMIT invertedChanged(); 66 | invalidate(); 67 | } 68 | 69 | bool Filter::filterAcceptsRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 70 | { 71 | return !m_enabled || filterRow(sourceIndex, proxyModel) ^ m_inverted; 72 | } 73 | 74 | void Filter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 75 | { 76 | Q_UNUSED(proxyModel) 77 | } 78 | 79 | void Filter::invalidate() 80 | { 81 | if (m_enabled) 82 | Q_EMIT invalidated(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /proxyroles/joinrole.cpp: -------------------------------------------------------------------------------- 1 | #include "joinrole.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype JoinRole 8 | \inherits SingleRole 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup ProxyRoles 11 | \brief a role made from concatenating other roles. 12 | 13 | A JoinRole is a simple \l ProxyRole that concatenates other roles. 14 | 15 | In the following example, the \c fullName role is computed by the concatenation of the \c firstName role and the \c lastName role separated by a space : 16 | \code 17 | SortFilterProxyModel { 18 | sourceModel: contactModel 19 | proxyRoles: JoinRole { 20 | name: "fullName" 21 | roleNames: ["firstName", "lastName"] 22 | } 23 | } 24 | \endcode 25 | 26 | */ 27 | 28 | /*! 29 | \qmlproperty list JoinRole::roleNames 30 | 31 | This property holds the role names that are joined by this role. 32 | */ 33 | QStringList JoinRole::roleNames() const 34 | { 35 | return m_roleNames; 36 | } 37 | 38 | void JoinRole::setRoleNames(const QStringList& roleNames) 39 | { 40 | if (m_roleNames == roleNames) 41 | return; 42 | 43 | m_roleNames = roleNames; 44 | Q_EMIT roleNamesChanged(); 45 | invalidate(); 46 | } 47 | 48 | /*! 49 | \qmlproperty string JoinRole::separator 50 | 51 | This property holds the separator that is used to join the roles specified in \l roleNames. 52 | 53 | By default, it's a space. 54 | */ 55 | QString JoinRole::separator() const 56 | { 57 | return m_separator; 58 | } 59 | 60 | void JoinRole::setSeparator(const QString& separator) 61 | { 62 | if (m_separator == separator) 63 | return; 64 | 65 | m_separator = separator; 66 | Q_EMIT separatorChanged(); 67 | invalidate(); 68 | } 69 | 70 | QVariant JoinRole::data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) 71 | { 72 | QString result; 73 | 74 | for (const QString& roleName : m_roleNames) 75 | result += proxyModel.sourceData(sourceIndex, roleName).toString() + m_separator; 76 | 77 | if (!m_roleNames.isEmpty()) 78 | result.chop(m_separator.length()); 79 | 80 | return result; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /SortFilterProxyModel.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | Group { 4 | name: "SortFilterProxyModel" 5 | prefix: path + "/" 6 | files: [ 7 | "filters/alloffilter.cpp", 8 | "filters/alloffilter.h", 9 | "filters/anyoffilter.cpp", 10 | "filters/anyoffilter.h", 11 | "filters/expressionfilter.cpp", 12 | "filters/expressionfilter.h", 13 | "filters/filter.cpp", 14 | "filters/filter.h", 15 | "filters/filtercontainer.cpp", 16 | "filters/filtercontainer.h", 17 | "filters/filtercontainerfilter.cpp", 18 | "filters/filtercontainerfilter.h", 19 | "filters/filtersqmltypes.cpp", 20 | "filters/indexfilter.cpp", 21 | "filters/indexfilter.h", 22 | "filters/rangefilter.cpp", 23 | "filters/rangefilter.h", 24 | "filters/regexpfilter.cpp", 25 | "filters/regexpfilter.h", 26 | "filters/rolefilter.cpp", 27 | "filters/rolefilter.h", 28 | "filters/valuefilter.cpp", 29 | "filters/valuefilter.h", 30 | "proxyroles/expressionrole.cpp", 31 | "proxyroles/expressionrole.h", 32 | "proxyroles/filterrole.cpp", 33 | "proxyroles/filterrole.h", 34 | "proxyroles/joinrole.cpp", 35 | "proxyroles/joinrole.h", 36 | "proxyroles/proxyrole.cpp", 37 | "proxyroles/proxyrole.h", 38 | "proxyroles/proxyrolecontainer.cpp", 39 | "proxyroles/proxyrolecontainer.h", 40 | "proxyroles/proxyrolesqmltypes.cpp", 41 | "proxyroles/regexprole.cpp", 42 | "proxyroles/regexprole.h", 43 | "proxyroles/singlerole.cpp", 44 | "proxyroles/singlerole.h", 45 | "proxyroles/switchrole.cpp", 46 | "proxyroles/switchrole.h", 47 | "sorters/expressionsorter.cpp", 48 | "sorters/expressionsorter.h", 49 | "sorters/filtersorter.cpp", 50 | "sorters/filtersorter.h", 51 | "sorters/rolesorter.cpp", 52 | "sorters/rolesorter.h", 53 | "sorters/sorter.cpp", 54 | "sorters/sorter.h", 55 | "sorters/sortercontainer.cpp", 56 | "sorters/sortercontainer.h", 57 | "sorters/sortersqmltypes.cpp", 58 | "sorters/stringsorter.cpp", 59 | "sorters/stringsorter.h", 60 | "utils/utils.cpp", 61 | "utils/utils.h", 62 | "qqmlsortfilterproxymodel.cpp", 63 | "qqmlsortfilterproxymodel.h" 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /sorters/filtersorter.cpp: -------------------------------------------------------------------------------- 1 | #include "filtersorter.h" 2 | #include "filters/filter.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype FilterSorter 8 | \inherits Sorter 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Sorters 11 | \ingroup FilterContainer 12 | \brief Sorts rows based on if they match filters. 13 | 14 | A FilterSorter is a \l Sorter that orders row matching its filters before the rows not matching the filters. 15 | 16 | In the following example, rows with their \c favorite role set to \c true will be ordered at the beginning : 17 | \code 18 | SortFilterProxyModel { 19 | sourceModel: contactModel 20 | sorters: FilterSorter { 21 | ValueFilter { roleName: "favorite"; value: true } 22 | } 23 | } 24 | \endcode 25 | \sa FilterContainer 26 | */ 27 | 28 | /*! 29 | \qmlproperty list FilterSorter::filters 30 | \default 31 | 32 | This property holds the list of filters for this filter sorter. 33 | If a row match all this FilterSorter's filters, it will be ordered before rows not matching all the filters. 34 | 35 | \sa Filter, FilterContainer 36 | */ 37 | 38 | int FilterSorter::compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel &proxyModel) const 39 | { 40 | bool leftIsAccepted = indexIsAccepted(sourceLeft, proxyModel); 41 | bool rightIsAccepted = indexIsAccepted(sourceRight, proxyModel); 42 | 43 | if (leftIsAccepted == rightIsAccepted) 44 | return 0; 45 | 46 | return leftIsAccepted ? -1 : 1; 47 | } 48 | 49 | void FilterSorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 50 | { 51 | for (Filter* filter : m_filters) 52 | filter->proxyModelCompleted(proxyModel); 53 | } 54 | 55 | void FilterSorter::onFilterAppended(Filter* filter) 56 | { 57 | connect(filter, &Filter::invalidated, this, &FilterSorter::invalidate); 58 | invalidate(); 59 | } 60 | 61 | void FilterSorter::onFilterRemoved(Filter* filter) 62 | { 63 | disconnect(filter, &Filter::invalidated, this, &FilterSorter::invalidate); 64 | invalidate(); 65 | } 66 | 67 | void FilterSorter::onFiltersCleared() 68 | { 69 | invalidate(); 70 | } 71 | 72 | bool FilterSorter::indexIsAccepted(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 73 | { 74 | return std::all_of(m_filters.begin(), m_filters.end(), 75 | [&] (Filter* filter) { 76 | return filter->filterAcceptsRow(sourceIndex, proxyModel); 77 | } 78 | ); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /SortFilterProxyModel.pri: -------------------------------------------------------------------------------- 1 | !contains( CONFIG, c\+\+1[147] ): warning("SortFilterProxyModel needs at least c++11, add CONFIG += c++11 to your .pro") 2 | 3 | INCLUDEPATH += $$PWD 4 | 5 | HEADERS += $$PWD/qqmlsortfilterproxymodel.h \ 6 | $$PWD/qvariantlessthan.h \ 7 | $$PWD/filters/filter.h \ 8 | $$PWD/filters/filtercontainer.h \ 9 | $$PWD/filters/rolefilter.h \ 10 | $$PWD/filters/valuefilter.h \ 11 | $$PWD/filters/indexfilter.h \ 12 | $$PWD/filters/regexpfilter.h \ 13 | $$PWD/filters/rangefilter.h \ 14 | $$PWD/filters/expressionfilter.h \ 15 | $$PWD/filters/filtercontainerfilter.h \ 16 | $$PWD/filters/anyoffilter.h \ 17 | $$PWD/filters/alloffilter.h \ 18 | $$PWD/sorters/sorter.h \ 19 | $$PWD/sorters/sortercontainer.h \ 20 | $$PWD/sorters/rolesorter.h \ 21 | $$PWD/sorters/stringsorter.h \ 22 | $$PWD/sorters/expressionsorter.h \ 23 | $$PWD/proxyroles/proxyrole.h \ 24 | $$PWD/proxyroles/proxyrolecontainer.h \ 25 | $$PWD/proxyroles/joinrole.h \ 26 | $$PWD/proxyroles/switchrole.h \ 27 | $$PWD/proxyroles/expressionrole.h \ 28 | $$PWD/proxyroles/singlerole.h \ 29 | $$PWD/proxyroles/regexprole.h \ 30 | $$PWD/sorters/filtersorter.h \ 31 | $$PWD/proxyroles/filterrole.h \ 32 | $$PWD/utils/utils.h 33 | 34 | SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \ 35 | $$PWD/qvariantlessthan.cpp \ 36 | $$PWD/filters/filter.cpp \ 37 | $$PWD/filters/filtercontainer.cpp \ 38 | $$PWD/filters/rolefilter.cpp \ 39 | $$PWD/filters/valuefilter.cpp \ 40 | $$PWD/filters/indexfilter.cpp \ 41 | $$PWD/filters/regexpfilter.cpp \ 42 | $$PWD/filters/rangefilter.cpp \ 43 | $$PWD/filters/expressionfilter.cpp \ 44 | $$PWD/filters/filtercontainerfilter.cpp \ 45 | $$PWD/filters/anyoffilter.cpp \ 46 | $$PWD/filters/alloffilter.cpp \ 47 | $$PWD/filters/filtersqmltypes.cpp \ 48 | $$PWD/sorters/sorter.cpp \ 49 | $$PWD/sorters/sortercontainer.cpp \ 50 | $$PWD/sorters/rolesorter.cpp \ 51 | $$PWD/sorters/stringsorter.cpp \ 52 | $$PWD/sorters/expressionsorter.cpp \ 53 | $$PWD/sorters/sortersqmltypes.cpp \ 54 | $$PWD/proxyroles/proxyrole.cpp \ 55 | $$PWD/proxyroles/proxyrolecontainer.cpp \ 56 | $$PWD/proxyroles/joinrole.cpp \ 57 | $$PWD/proxyroles/switchrole.cpp \ 58 | $$PWD/proxyroles/expressionrole.cpp \ 59 | $$PWD/proxyroles/proxyrolesqmltypes.cpp \ 60 | $$PWD/proxyroles/singlerole.cpp \ 61 | $$PWD/proxyroles/regexprole.cpp \ 62 | $$PWD/sorters/filtersorter.cpp \ 63 | $$PWD/proxyroles/filterrole.cpp \ 64 | $$PWD/utils/utils.cpp 65 | -------------------------------------------------------------------------------- /tests/tst_rolesorter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import SortFilterProxyModel 0.2 3 | import QtQml.Models 2.2 4 | import QtTest 1.1 5 | 6 | Item { 7 | property list sorters: [ 8 | RoleSorter { 9 | property string tag: "intRole" 10 | property var expectedValues: [1, 2, 3, 4, 5] 11 | roleName: "intRole" 12 | }, 13 | RoleSorter { 14 | property string tag: "intRoleDescending" 15 | property var expectedValues: [5, 4, 3, 2, 1] 16 | roleName: "intRole" 17 | sortOrder: Qt.DescendingOrder 18 | }, 19 | RoleSorter { 20 | property string tag: "stringRole" 21 | property var expectedValues: ["a", "b", "c", "d", "e"] 22 | roleName: "stringRole" 23 | }, 24 | RoleSorter { 25 | property string tag: "stringRoleDescending" 26 | property var expectedValues: ["e", "d", "c", "b", "a"] 27 | roleName: "stringRole" 28 | sortOrder: Qt.DescendingOrder 29 | }, 30 | RoleSorter { 31 | property string tag: "mixedCaseStringRole" 32 | property var expectedValues: ["A", "b", "C", "D", "e"] 33 | roleName: "mixedCaseStringRole" 34 | } 35 | ] 36 | 37 | ListModel { 38 | id: dataModel 39 | ListElement { intRole: 5; stringRole: "c"; mixedCaseStringRole: "C" } 40 | ListElement { intRole: 3; stringRole: "e"; mixedCaseStringRole: "e" } 41 | ListElement { intRole: 1; stringRole: "d"; mixedCaseStringRole: "D" } 42 | ListElement { intRole: 2; stringRole: "a"; mixedCaseStringRole: "A" } 43 | ListElement { intRole: 4; stringRole: "b"; mixedCaseStringRole: "b" } 44 | } 45 | 46 | SortFilterProxyModel { 47 | id: testModel 48 | sourceModel: dataModel 49 | } 50 | 51 | TestCase { 52 | name: "RoleSorterTests" 53 | 54 | function test_roleSorters_data() { 55 | return sorters; 56 | } 57 | 58 | function test_roleSorters(sorter) { 59 | testModel.sorters = sorter; 60 | 61 | verify(testModel.count === sorter.expectedValues.length, 62 | "Expected count " + sorter.expectedValues.length + ", actual count: " + testModel.count); 63 | let actualValues = []; 64 | for (var i = 0; i < testModel.count; i++) { 65 | actualValues.push(testModel.get(i, sorter.roleName)); 66 | } 67 | compare(actualValues, sorter.expectedValues); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /filters/regexpfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "regexpfilter.h" 2 | #include 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype RegExpFilter 8 | \inherits RoleFilter 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Filters 11 | \brief Filters rows matching a regular expression. 12 | 13 | A RegExpFilter is a \l RoleFilter that accepts rows matching a regular rexpression. 14 | 15 | In the following example, only rows with their \c lastName role beggining with the content of textfield the will be accepted: 16 | \code 17 | TextField { 18 | id: nameTextField 19 | } 20 | 21 | SortFilterProxyModel { 22 | sourceModel: contactModel 23 | filters: RegExpFilter { 24 | roleName: "lastName" 25 | pattern: "^" + nameTextField.displayText 26 | } 27 | } 28 | \endcode 29 | */ 30 | 31 | /*! 32 | \qmlproperty bool RegExpFilter::pattern 33 | 34 | The pattern used to filter the contents of the source model. 35 | 36 | \sa syntax 37 | */ 38 | RegExpFilter::RegExpFilter() : 39 | m_caseSensitivity(m_regExp.patternOptions().testFlag( 40 | QRegularExpression::CaseInsensitiveOption) ? Qt::CaseInsensitive : Qt::CaseSensitive) 41 | { 42 | } 43 | 44 | QString RegExpFilter::pattern() const 45 | { 46 | return m_pattern; 47 | } 48 | 49 | void RegExpFilter::setPattern(const QString& pattern) 50 | { 51 | if (m_pattern == pattern) 52 | return; 53 | 54 | m_pattern = pattern; 55 | m_regExp.setPattern(pattern); 56 | Q_EMIT patternChanged(); 57 | invalidate(); 58 | } 59 | 60 | /*! 61 | \qmlproperty Qt::CaseSensitivity RegExpFilter::caseSensitivity 62 | 63 | This property holds the caseSensitivity of the filter. 64 | */ 65 | Qt::CaseSensitivity RegExpFilter::caseSensitivity() const 66 | { 67 | return m_caseSensitivity; 68 | } 69 | 70 | void RegExpFilter::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 71 | { 72 | if (m_caseSensitivity == caseSensitivity) 73 | return; 74 | 75 | m_caseSensitivity = caseSensitivity; 76 | QRegularExpression::PatternOptions patternOptions = m_regExp.patternOptions(); 77 | if (caseSensitivity == Qt::CaseInsensitive) 78 | patternOptions.setFlag(QRegularExpression::CaseInsensitiveOption); 79 | m_regExp.setPatternOptions(patternOptions); 80 | Q_EMIT caseSensitivityChanged(); 81 | invalidate(); 82 | } 83 | 84 | bool RegExpFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 85 | { 86 | const QString string = sourceData(sourceIndex, proxyModel).toString(); 87 | return m_regExp.match(string).hasMatch(); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-sortfilterproxymodel-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | List of All Members for SortFilterProxyModel | SortFilterProxyModel 8 | 9 | 10 | 11 | 12 |

List of All Members for SortFilterProxyModel

13 |

This is the complete list of members for SortFilterProxyModel, including inherited members.

14 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /utils/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | int compareVariants(const QVariant &lhs, const QVariant &rhs) 8 | { 9 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // qt 5 10 | // Do the QString check first because otherwise the canConvert check will get hit for strings. 11 | if (static_cast(lhs.type()) == QMetaType::QString && static_cast(rhs.type()) == QMetaType::QString) { 12 | const auto lhsValue = lhs.toString(); 13 | const auto rhsValue = rhs.toString(); 14 | if (lhsValue == rhsValue) 15 | return 0; 16 | return lhsValue.compare(rhsValue, Qt::CaseInsensitive); 17 | } else if (static_cast(lhs.type()) == QMetaType::Bool && static_cast(rhs.type()) == QMetaType::Bool) { 18 | const auto lhsValue = lhs.toBool(); 19 | const auto rhsValue = rhs.toBool(); 20 | if (lhsValue == rhsValue) 21 | return 0; 22 | // false < true. 23 | return !lhsValue ? -1 : 1; 24 | } else if (static_cast(lhs.type()) == QMetaType::QDate && static_cast(rhs.type()) == QMetaType::QDate) { 25 | const auto lhsValue = lhs.toDate(); 26 | const auto rhsValue = rhs.toDate(); 27 | if (lhsValue == rhsValue) 28 | return 0; 29 | return lhsValue < rhsValue ? -1 : 1; 30 | } else if (static_cast(lhs.type()) == QMetaType::QDateTime && static_cast(rhs.type()) == QMetaType::QDateTime) { 31 | const auto lhsValue = lhs.toDateTime(); 32 | const auto rhsValue = rhs.toDateTime(); 33 | if (lhsValue == rhsValue) 34 | return 0; 35 | return lhsValue < rhsValue ? -1 : 1; 36 | } else if (static_cast(lhs.type()) == QMetaType::QStringList && static_cast(rhs.type()) == QMetaType::QStringList) { 37 | const auto lhsValue = lhs.toStringList(); 38 | const auto rhsValue = rhs.toStringList(); 39 | if (lhsValue == rhsValue) 40 | return 0; 41 | return lhsValue < rhsValue ? -1 : 1; 42 | } else if (lhs.canConvert() && rhs.canConvert()) { 43 | const auto lhsValue = lhs.toInt(); 44 | const auto rhsValue = rhs.toInt(); 45 | if (lhsValue == rhsValue) 46 | return 0; 47 | return lhsValue < rhsValue ? -1 : 1; 48 | } else if (lhs.canConvert() && rhs.canConvert()) { 49 | const auto lhsValue = lhs.toReal(); 50 | const auto rhsValue = rhs.toReal(); 51 | if (qFuzzyCompare(lhsValue, rhsValue)) 52 | return 0; 53 | return lhsValue < rhsValue ? -1 : 1; 54 | } 55 | 56 | qWarning() << "Don't know how to compare" << lhs << "against" << rhs << "- returning 0"; 57 | return 0; 58 | #else 59 | return QPartialOrdering::Less == QVariant::compare(lhs, rhs); 60 | #endif 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /qvariantlessthan.cpp: -------------------------------------------------------------------------------- 1 | #include "qvariantlessthan.h" 2 | 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \brief Less-than operator for generic QVariants 9 | 10 | Since Qt 5.15 deprecated the less-than operator of QVariant, we 11 | have to provide our own implementation. On older Qt versions, 12 | use the original implementation. 13 | 14 | Includes special implementations for numberic types, char, date and 15 | time. Everything else is converted to String and compared then. 16 | */ 17 | bool lessThan(const QVariant &lhs, const QVariant &rhs) 18 | { 19 | #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 20 | return lhs < rhs; 21 | #else 22 | static const auto numericTypes = QVector{ 23 | QMetaType::Int, 24 | QMetaType::UInt, 25 | QMetaType::LongLong, 26 | QMetaType::ULongLong, 27 | QMetaType::Float, 28 | QMetaType::Double, 29 | }; 30 | static const auto unsignedTypes = QVector{ 31 | QMetaType::UInt, 32 | QMetaType::ULongLong, 33 | }; 34 | static const auto dateTimeTypes = QVector{ 35 | QMetaType::QDate, 36 | QMetaType::QTime, 37 | QMetaType::QDateTime, 38 | }; 39 | 40 | const auto lt = static_cast(lhs.userType()); 41 | const auto rt = static_cast(rhs.userType()); 42 | if (numericTypes.contains(lt) && numericTypes.contains(rt)) { 43 | if (lt == QMetaType::Double || lt == QMetaType::Float 44 | || rt == QMetaType::Double || rt == QMetaType::Float) { 45 | return lhs.toDouble() < rhs.toDouble(); 46 | } else { 47 | const auto ul = unsignedTypes.contains(lt); 48 | const auto ur = unsignedTypes.contains(rt); 49 | if (ul && ur) { 50 | return lhs.toULongLong() < rhs.toULongLong(); 51 | } else if (!ul && !ur) { 52 | return lhs.toLongLong() < rhs.toLongLong(); 53 | } else if (ul) { 54 | const auto r = rhs.toLongLong(); 55 | return r > 0 && 56 | lhs.toULongLong() < static_cast(r); 57 | } else { 58 | const auto l = lhs.toLongLong(); 59 | return l < 0 || 60 | static_cast(l) < rhs.toULongLong(); 61 | } 62 | } 63 | } else if (dateTimeTypes.contains(lt) && dateTimeTypes.contains(rt)) { 64 | if (lt == QMetaType::QDate && rt == QMetaType::QDate) { 65 | return lhs.toDate() < rhs.toDate(); 66 | } else if (lt == QMetaType::QTime && rt == QMetaType::QTime) { 67 | return lhs.toTime() < rhs.toTime(); 68 | } else { 69 | return lhs.toDateTime() < rhs.toDateTime(); 70 | } 71 | } else if (lt == QMetaType::Char && rt == QMetaType::Char) { 72 | return lhs.toChar() < rhs.toChar(); 73 | } else { 74 | return lhs.toString() < rhs.toString(); 75 | } 76 | #endif 77 | } 78 | 79 | } // namespace qqsfpm 80 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-singlerole.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SingleRole QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

SingleRole QML Type

21 | 22 | 23 |

Base type for the SortFilterProxyModel proxy roles defining a single role. More...

24 | 25 |
26 |
Import Statement: import SortFilterProxyModel .
Inherits:

ProxyRole

27 |
Inherited By:

ExpressionRole, FilterRole, JoinRole, and SwitchRole

28 |
31 | 32 |

Properties

33 |
    34 |
  • name : string
  • 35 |
36 | 37 | 38 |

Detailed Description

39 |

SingleRole is a convenience base class for proxy roles who define a single role. It cannot be used directly in a QML file. It exists to provide a set of common properties and methods, available across all the other proxy role types that inherit from it. Attempting to use the SingleRole type directly will result in an error.

40 | 41 |

Property Documentation

42 | 43 |
44 |
45 | 46 | 48 |

47 | name : string

49 |

This property holds the role name of the proxy role.

50 |
51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-sortercontainer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SorterContainer QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 21 |

SorterContainer QML Type

22 | 23 | 24 |

Abstract interface for types containing Sorters. More...

25 | 26 |
27 |
Import Statement: import SortFilterProxyModel .
30 | 31 |

Attached Properties

32 | 35 | 36 | 37 |

Detailed Description

38 | 39 |

Types implementing this interface:

40 |
41 | 42 |

SortFilterProxyModel

Filters and sorts data coming from a source QAbstractItemModel

43 | 44 |

Attached Property Documentation

45 | 46 |
47 |
48 | 49 | 51 |

50 | SorterContainer.container : bool

52 |

This attached property allows you to include in a SorterContainer a Sorter that has been instantiated outside of the SorterContainer, for example in an Instantiator.

53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /tests/tst_filtercontainers.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import SortFilterProxyModel 0.2 3 | import QtQml.Models 2.2 4 | import QtTest 1.1 5 | 6 | Item { 7 | property list filters: [ 8 | AllOf { 9 | property string tag: "allOf" 10 | property var expectedValues: [{a: 0, b: false}] 11 | ValueFilter { 12 | roleName: "a" 13 | value: "0" 14 | } 15 | ValueFilter { 16 | roleName: "b" 17 | value: false 18 | } 19 | }, 20 | AllOf { 21 | property string tag: "allOfOneDisabled" 22 | property var expectedValues: [{a: 0, b: true}, {a: 0, b: false}] 23 | ValueFilter { 24 | roleName: "a" 25 | value: "0" 26 | } 27 | ValueFilter { 28 | enabled: false 29 | roleName: "b" 30 | value: false 31 | } 32 | }, 33 | AnyOf { 34 | property string tag: "anyOf" 35 | property var expectedValues: [{a: 0, b: true}, {a: 0, b: false}, {a: 1, b: false}] 36 | ValueFilter { 37 | roleName: "a" 38 | value: "0" 39 | } 40 | ValueFilter { 41 | roleName: "b" 42 | value: false 43 | } 44 | } 45 | ] 46 | 47 | AllOf { 48 | id: outerFilter 49 | ValueFilter { 50 | roleName: "a" 51 | value: "0" 52 | } 53 | ValueFilter { 54 | id: innerFilter 55 | roleName: "b" 56 | value: false 57 | } 58 | } 59 | 60 | ListModel { 61 | id: dataModel 62 | ListElement { a: 0; b: true } 63 | ListElement { a: 0; b: false } 64 | ListElement { a: 1; b: true } 65 | ListElement { a: 1; b: false } 66 | } 67 | 68 | SortFilterProxyModel { 69 | id: testModel 70 | sourceModel: dataModel 71 | } 72 | 73 | TestCase { 74 | name:"RangeFilterTests" 75 | 76 | function modelValues() { 77 | var modelValues = []; 78 | 79 | for (var i = 0; i < testModel.count; i++) 80 | modelValues.push(testModel.get(i)); 81 | 82 | return modelValues; 83 | } 84 | 85 | function test_filterContainers_data() { 86 | return filters; 87 | } 88 | 89 | function test_filterContainers(filter) { 90 | testModel.filters = filter; 91 | compare(JSON.stringify(modelValues()), JSON.stringify(filter.expectedValues)); 92 | } 93 | 94 | function test_changeInnerFilter() { 95 | testModel.filters = outerFilter; 96 | compare(JSON.stringify(modelValues()), JSON.stringify([{a: 0, b: false}])); 97 | innerFilter.value = true; 98 | compare(JSON.stringify(modelValues()), JSON.stringify([{a: 0, b: true}])); 99 | innerFilter.enabled = false; 100 | compare(JSON.stringify(modelValues()), JSON.stringify([{a: 0, b: true}, {a: 0, b: false}])); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /filters/indexfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "indexfilter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype IndexFilter 8 | \inherits Filter 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Filters 11 | \brief Filters rows based on their source index. 12 | 13 | An IndexFilter is a filter allowing contents to be filtered based on their source model index. 14 | 15 | In the following example, only the first row of the source model will be accepted: 16 | \code 17 | SortFilterProxyModel { 18 | sourceModel: contactModel 19 | filters: IndexFilter { 20 | maximumIndex: 0 21 | } 22 | } 23 | \endcode 24 | */ 25 | 26 | /*! 27 | \qmlproperty int IndexFilter::minimumIndex 28 | 29 | This property holds the minimumIndex of the filter. 30 | Rows with a source index lower than \c minimumIndex will be rejected. 31 | 32 | If \c minimumIndex is negative, it is counted from the end of the source model, meaning that : 33 | \code 34 | minimumIndex: -1 35 | \endcode 36 | is equivalent to : 37 | \code 38 | minimumIndex: sourceModel.count - 1 39 | \endcode 40 | By default, no value is set. 41 | */ 42 | const QVariant& IndexFilter::minimumIndex() const 43 | { 44 | return m_minimumIndex; 45 | } 46 | 47 | void IndexFilter::setMinimumIndex(const QVariant& minimumIndex) 48 | { 49 | if (m_minimumIndex == minimumIndex) 50 | return; 51 | 52 | m_minimumIndex = minimumIndex; 53 | Q_EMIT minimumIndexChanged(); 54 | invalidate(); 55 | } 56 | 57 | /*! 58 | \qmlproperty int IndexFilter::maximumIndex 59 | 60 | This property holds the maximumIndex of the filter. 61 | Rows with a source index higher than \c maximumIndex will be rejected. 62 | 63 | If \c maximumIndex is negative, it is counted from the end of the source model, meaning that: 64 | \code 65 | maximumIndex: -1 66 | \endcode 67 | is equivalent to : 68 | \code 69 | maximumIndex: sourceModel.count - 1 70 | \endcode 71 | By default, no value is set. 72 | */ 73 | const QVariant& IndexFilter::maximumIndex() const 74 | { 75 | return m_maximumIndex; 76 | } 77 | 78 | void IndexFilter::setMaximumIndex(const QVariant& maximumIndex) 79 | { 80 | if (m_maximumIndex == maximumIndex) 81 | return; 82 | 83 | m_maximumIndex = maximumIndex; 84 | Q_EMIT maximumIndexChanged(); 85 | invalidate(); 86 | } 87 | 88 | bool IndexFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 89 | { 90 | int sourceRowCount = proxyModel.sourceModel()->rowCount(); 91 | int sourceRow = sourceIndex.row(); 92 | 93 | bool minimumIsValid; 94 | int minimum = m_minimumIndex.toInt(&minimumIsValid); 95 | if (minimumIsValid) { 96 | int actualMinimum = minimum < 0 ? sourceRowCount + minimum : minimum; 97 | if (sourceRow < actualMinimum) 98 | return false; 99 | } 100 | 101 | bool maximumIsValid; 102 | int maximum = m_maximumIndex.toInt(&maximumIsValid); 103 | if (maximumIsValid) { 104 | int actualMaximum = maximum < 0 ? sourceRowCount + maximum : maximum; 105 | if (sourceRow > actualMaximum) 106 | return false; 107 | } 108 | 109 | return true; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /tests/tst_stringsorter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import SortFilterProxyModel 0.2 3 | import QtQml.Models 2.2 4 | import QtTest 1.1 5 | 6 | Item { 7 | property list sorters: [ 8 | StringSorter { 9 | property string tag: "normal" 10 | property var expectedValues: ["haha", "hähä", "hehe", "héhé", "hihi", "huhu"] 11 | roleName: "accentRole" 12 | }, 13 | StringSorter { 14 | property string tag: "numericMode" 15 | property var expectedValues: ["a1", "a20", "a30", "a99", "a100", "a1000"] 16 | roleName: "numericRole" 17 | numericMode: true 18 | }, 19 | StringSorter { 20 | property string tag: "nonNumericMode" 21 | property var expectedValues: ["a1", "a100", "a1000", "a20", "a30", "a99"] 22 | roleName: "numericRole" 23 | numericMode: false 24 | }, 25 | StringSorter { 26 | property string tag: "caseSensitive" 27 | property var expectedValues: ["a", "A", "b", "c", "z", "Z"] 28 | roleName: "caseRole" 29 | caseSensitivity: Qt.CaseSensitive 30 | }, 31 | StringSorter { 32 | property string tag: "nonCaseSensitive" 33 | property var expectedValues: ["A", "a", "b", "c", "Z", "z"] 34 | roleName: "caseRole" 35 | caseSensitivity: Qt.CaseInsensitive 36 | }, 37 | StringSorter { 38 | property string tag: "ignorePunctuation" 39 | property var expectedValues: ["a-a", "aa", "b-b", "b-c", "b.c", "bc"] 40 | roleName: "punctuationRole" 41 | ignorePunctation: true 42 | }, 43 | StringSorter { 44 | property string tag: "doNotIgnorePunctuation" 45 | property var expectedValues: ["aa", "a-a", "b.c", "b-b", "bc", "b-c"] 46 | roleName: "punctuationRole" 47 | ignorePunctation: false 48 | } 49 | ] 50 | 51 | ListModel { 52 | id: dataModel 53 | ListElement { accentRole: "héhé"; numericRole: "a20"; caseRole: "b"; punctuationRole: "a-a"} 54 | ListElement { accentRole: "hehe"; numericRole: "a1"; caseRole: "A"; punctuationRole: "aa"} 55 | ListElement { accentRole: "haha"; numericRole: "a100"; caseRole: "a"; punctuationRole: "b-c"} 56 | ListElement { accentRole: "huhu"; numericRole: "a99"; caseRole: "c"; punctuationRole: "b.c"} 57 | ListElement { accentRole: "hihi"; numericRole: "a30"; caseRole: "Z"; punctuationRole: "bc"} 58 | ListElement { accentRole: "hähä"; numericRole: "a1000"; caseRole: "z"; punctuationRole: "b-b"} 59 | } 60 | 61 | SortFilterProxyModel { 62 | id: testModel 63 | sourceModel: dataModel 64 | } 65 | 66 | TestCase { 67 | name: "StringSorterTests" 68 | 69 | function test_stringSorters_data() { 70 | return sorters; 71 | } 72 | 73 | function test_stringSorters(sorter) { 74 | testModel.sorters = sorter; 75 | 76 | verify(testModel.count === sorter.expectedValues.length, 77 | "Expected count " + sorter.expectedValues.length + ", actual count: " + testModel.count); 78 | let actualValues = []; 79 | for (var i = 0; i < testModel.count; i++) { 80 | actualValues.push(testModel.get(i, sorter.roleName)); 81 | } 82 | compare(actualValues, sorter.expectedValues); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/tst_switchrole.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import QtQml 2.2 6 | 7 | Item { 8 | ListModel { 9 | id: listModel 10 | ListElement { name: "1"; favorite: true } 11 | ListElement { name: "2"; favorite: false } 12 | ListElement { name: "3"; favorite: false } 13 | ListElement { name: "4"; favorite: true } 14 | } 15 | 16 | SortFilterProxyModel { 17 | id: testModel 18 | sourceModel: listModel 19 | 20 | proxyRoles: SwitchRole { 21 | id: switchRole 22 | name: "switchRole" 23 | ValueFilter { 24 | id: valueFilter 25 | roleName: "favorite" 26 | value: true 27 | SwitchRole.value: "*" 28 | } 29 | ValueFilter { 30 | id: secondValueFilter 31 | roleName: "favorite" 32 | value: true 33 | SwitchRole.value: "%" 34 | } 35 | ValueFilter { 36 | id: thirdValueFilter 37 | roleName: "name" 38 | value: 3 39 | SwitchRole.value: "three" 40 | } 41 | defaultRoleName: "name" 42 | defaultValue: "foo" 43 | } 44 | } 45 | 46 | Instantiator { 47 | id: instantiator 48 | model: testModel 49 | QtObject { 50 | property var switchRole: model.switchRole 51 | } 52 | } 53 | 54 | TestCase { 55 | name: "SwitchRole" 56 | 57 | function test_role() { 58 | compare(testModel.get(0, "switchRole"), "*"); 59 | compare(testModel.get(1, "switchRole"), "2"); 60 | compare(testModel.get(2, "switchRole"), "three"); 61 | compare(testModel.get(3, "switchRole"), "*"); 62 | } 63 | 64 | function test_valueChange() { 65 | compare(instantiator.object.switchRole, "*"); 66 | valueFilter.SwitchRole.value = "test"; 67 | compare(instantiator.object.switchRole, "test"); 68 | valueFilter.SwitchRole.value = "*"; 69 | } 70 | 71 | function test_filterChange() { 72 | compare(instantiator.object.switchRole, "*"); 73 | valueFilter.enabled = false; 74 | compare(instantiator.object.switchRole, "%"); 75 | valueFilter.enabled = true; 76 | } 77 | 78 | function test_defaultSourceChange() { 79 | compare(instantiator.object.switchRole, "*"); 80 | listModel.setProperty(0, "favorite", false); 81 | compare(instantiator.object.switchRole, "1"); 82 | compare(instantiator.objectAt(1).switchRole, "2"); 83 | listModel.setProperty(1, "name", "test"); 84 | compare(instantiator.objectAt(1).switchRole, "test"); 85 | 86 | listModel.setProperty(1, "name", "2"); 87 | listModel.setProperty(0, "favorite", true); 88 | } 89 | 90 | function test_defaultValue() { 91 | switchRole.defaultRoleName = ""; 92 | compare(instantiator.objectAt(1).switchRole, "foo"); 93 | switchRole.defaultValue = "bar"; 94 | compare(instantiator.objectAt(1).switchRole, "bar"); 95 | switchRole.defaultRoleName = "name"; 96 | switchRole.defaultValue = "foo"; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /proxyroles/regexprole.cpp: -------------------------------------------------------------------------------- 1 | #include "regexprole.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype RegExpRole 9 | \inherits ProxyRole 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup ProxyRoles 12 | \brief A ProxyRole extracting data from a source role via a regular expression. 13 | 14 | A RegExpRole is a \l ProxyRole that provides a role for each named capture group of its regular expression \l pattern. 15 | 16 | In the following example, the \c date role of the source model will be extracted in 3 roles in the proxy moodel: \c year, \c month and \c day. 17 | \code 18 | SortFilterProxyModel { 19 | sourceModel: eventModel 20 | proxyRoles: RegExpRole { 21 | roleName: "date" 22 | pattern: "(?\\d{4})-(?\\d{2})-(?\\d{2})" 23 | } 24 | } 25 | \endcode 26 | */ 27 | 28 | /*! 29 | \qmlproperty QString RegExpRole::roleName 30 | 31 | This property holds the role name that the RegExpRole is using to query the source model's data to extract new roles from. 32 | */ 33 | QString RegExpRole::roleName() const 34 | { 35 | return m_roleName; 36 | } 37 | 38 | void RegExpRole::setRoleName(const QString& roleName) 39 | { 40 | if (m_roleName == roleName) 41 | return; 42 | 43 | m_roleName = roleName; 44 | Q_EMIT roleNameChanged(); 45 | } 46 | 47 | /*! 48 | \qmlproperty QString RegExpRole::pattern 49 | 50 | This property holds the pattern of the regular expression of this RegExpRole. 51 | The RegExpRole will expose a role for each of the named capture group of the pattern. 52 | */ 53 | QString RegExpRole::pattern() const 54 | { 55 | return m_regularExpression.pattern(); 56 | } 57 | 58 | void RegExpRole::setPattern(const QString& pattern) 59 | { 60 | if (m_regularExpression.pattern() == pattern) 61 | return; 62 | 63 | Q_EMIT namesAboutToBeChanged(); 64 | m_regularExpression.setPattern(pattern); 65 | invalidate(); 66 | Q_EMIT patternChanged(); 67 | Q_EMIT namesChanged(); 68 | } 69 | 70 | /*! 71 | \qmlproperty Qt::CaseSensitivity RegExpRole::caseSensitivity 72 | 73 | This property holds the caseSensitivity of the regular expression. 74 | */ 75 | Qt::CaseSensitivity RegExpRole::caseSensitivity() const 76 | { 77 | return m_regularExpression.patternOptions() & QRegularExpression::CaseInsensitiveOption ? 78 | Qt::CaseInsensitive : Qt::CaseSensitive; 79 | } 80 | 81 | void RegExpRole::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 82 | { 83 | if (this->caseSensitivity() == caseSensitivity) 84 | return; 85 | 86 | m_regularExpression.setPatternOptions(m_regularExpression.patternOptions() ^ QRegularExpression::CaseInsensitiveOption); //toggle the option 87 | Q_EMIT caseSensitivityChanged(); 88 | } 89 | 90 | QStringList RegExpRole::names() 91 | { 92 | QStringList nameCaptureGroups = m_regularExpression.namedCaptureGroups(); 93 | nameCaptureGroups.removeAll(""); 94 | return nameCaptureGroups; 95 | } 96 | 97 | QVariant RegExpRole::data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString &name) 98 | { 99 | QString text = proxyModel.sourceData(sourceIndex, m_roleName).toString(); 100 | QRegularExpressionMatch match = m_regularExpression.match(text); 101 | return match.hasMatch() ? (match.captured(name)) : QVariant{}; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /tests/tst_indexfilter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import SortFilterProxyModel 0.2 3 | import QtQml.Models 2.2 4 | import QtTest 1.1 5 | 6 | Item { 7 | property list filters: [ 8 | IndexFilter { 9 | property string tag: "basicUsage" 10 | property var expectedValues: [3, 1, 2] 11 | minimumIndex: 1; maximumIndex: 3 12 | }, 13 | IndexFilter { 14 | property string tag: "outOfBounds" 15 | property var expectedValues: [] 16 | minimumIndex: 3; maximumIndex: 1 17 | }, 18 | IndexFilter { 19 | property string tag: "0to0Inverted" 20 | property var expectedValues: [3,1,2,4] 21 | minimumIndex: 0; maximumIndex: 0; inverted: true 22 | }, 23 | IndexFilter { 24 | property string tag: "0to0" // bug / issue #15 25 | property var expectedValues: [5] 26 | minimumIndex: 0; maximumIndex: 0 27 | }, 28 | IndexFilter { 29 | property string tag: "basicUsageInverted" 30 | property var expectedValues: [5,4] 31 | minimumIndex: 1; maximumIndex: 3; inverted: true 32 | }, 33 | IndexFilter { 34 | property string tag: "last" 35 | property var expectedValues: [4] 36 | minimumIndex: -1 37 | }, 38 | IndexFilter { 39 | property string tag: "fromEnd" 40 | property var expectedValues: [2, 4] 41 | minimumIndex: -2 42 | }, 43 | IndexFilter { 44 | property string tag: "fromEndRange" 45 | property var expectedValues: [1, 2] 46 | minimumIndex: -3 47 | maximumIndex: -2 48 | }, 49 | IndexFilter { 50 | property string tag: "mixedSignRange" 51 | property var expectedValues: [3, 1, 2] 52 | minimumIndex: 1 53 | maximumIndex: -2 54 | }, 55 | IndexFilter { 56 | property string tag: "toBigFilter" 57 | property var expectedValues: [] 58 | minimumIndex: 5 59 | }, 60 | IndexFilter { 61 | property string tag: "noFilter" 62 | property var expectedValues: [5, 3, 1, 2, 4] 63 | }, 64 | IndexFilter { 65 | property string tag: "undefinedFilter" 66 | property var expectedValues: [5, 3, 1, 2, 4] 67 | minimumIndex: undefined 68 | maximumIndex: null 69 | } 70 | ] 71 | 72 | ListModel { 73 | id: dataModel 74 | ListElement { value: 5 } 75 | ListElement { value: 3 } 76 | ListElement { value: 1 } 77 | ListElement { value: 2 } 78 | ListElement { value: 4 } 79 | } 80 | 81 | SortFilterProxyModel { 82 | id: testModel 83 | // FIXME: Crashes/fails with error if I define ListModel directly within sourceModel 84 | sourceModel: dataModel 85 | } 86 | 87 | TestCase { 88 | name: "IndexFilterTests" 89 | 90 | function test_minMax_data() { 91 | return filters; 92 | } 93 | 94 | function test_minMax(filter) { 95 | testModel.filters = filter; 96 | 97 | var actualValues = []; 98 | for (var i = 0; i < testModel.count; i++) 99 | actualValues.push(testModel.data(testModel.index(i, 0))); 100 | 101 | compare(actualValues, filter.expectedValues); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-allof.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AllOf QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

AllOf QML Type

21 | 22 | 23 |

Filter container accepting rows accepted by all its child filters. More...

24 | 25 |
26 |
Import Statement: import SortFilterProxyModel .
Inherits:

Filter

27 |
30 | 31 |

Properties

32 | 36 | 37 | 38 |

Detailed Description

39 |

The AllOf type is a Filter container that accepts rows if all of its contained (and enabled) filters accept them, or if it has no filter.

40 |

Using it as a top level filter has the same effect as putting all its child filters as top level filters. It can however be usefull to use an AllOf filter when nested in an AnyOf filter.

41 |

See also FilterContainer.

42 | 43 |

Property Documentation

44 | 45 |
46 |
47 | 48 | 50 |

49 | enabled : bool

51 |

This property holds whether the filter is enabled. A disabled filter will accept every rows unconditionally (even if it's inverted).

52 |

By default, filters are enabled.

53 |
54 |
55 | 56 |
57 |
58 | 59 | 61 |

60 | inverted : bool

62 |

This property holds whether the filter is inverted. When a filter is inverted, a row normally accepted would be rejected, and vice-versa.

63 |

By default, filters are not inverted.

64 |
65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-filter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Filter QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

Filter QML Type

21 | 22 | 23 |

Base type for the SortFilterProxyModel filters. More...

24 | 25 |
26 |
Import Statement: import SortFilterProxyModel .
Inherited By:

AllOf, AnyOf, ExpressionFilter, IndexFilter, and RoleFilter

27 |
30 | 31 |

Properties

32 | 36 | 37 | 38 |

Detailed Description

39 |

The Filter type cannot be used directly in a QML file. It exists to provide a set of common properties and methods, available across all the other filter types that inherit from it. Attempting to use the Filter type directly will result in an error.

40 | 41 |

Property Documentation

42 | 43 |
44 |
45 | 46 | 48 |

47 | enabled : bool

49 |

This property holds whether the filter is enabled. A disabled filter will accept every rows unconditionally (even if it's inverted).

50 |

By default, filters are enabled.

51 |
52 |
53 | 54 |
55 |
56 | 57 | 59 |

58 | inverted : bool

60 |

This property holds whether the filter is inverted. When a filter is inverted, a row normally accepted would be rejected, and vice-versa.

61 |

By default, filters are not inverted.

62 |
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /sorters/stringsorter.cpp: -------------------------------------------------------------------------------- 1 | #include "stringsorter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype StringSorter 7 | \inherits RoleSorter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Sorters 10 | \brief Sorts rows based on a source model string role. 11 | 12 | \l StringSorter is a specialized \l RoleSorter that sorts rows based on a source model string role. 13 | \l StringSorter compares strings according to a localized collation algorithm. 14 | 15 | In the following example, rows with be sorted by their \c lastName role : 16 | \code 17 | SortFilterProxyModel { 18 | sourceModel: contactModel 19 | sorters: StringSorter { roleName: "lastName" } 20 | } 21 | \endcode 22 | */ 23 | 24 | /*! 25 | \qmlproperty Qt.CaseSensitivity StringSorter::caseSensitivity 26 | 27 | This property holds the case sensitivity of the sorter. 28 | */ 29 | Qt::CaseSensitivity StringSorter::caseSensitivity() const 30 | { 31 | return m_collator.caseSensitivity(); 32 | } 33 | 34 | void StringSorter::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 35 | { 36 | if (m_collator.caseSensitivity() == caseSensitivity) 37 | return; 38 | 39 | m_collator.setCaseSensitivity(caseSensitivity); 40 | Q_EMIT caseSensitivityChanged(); 41 | invalidate(); 42 | } 43 | 44 | /*! 45 | \qmlproperty bool StringSorter::ignorePunctation 46 | 47 | This property holds whether the sorter ignores punctation. 48 | if \c ignorePunctuation is \c true, punctuation characters and symbols are ignored when determining sort order. 49 | 50 | \note This property is not currently supported on Apple platforms or if Qt is configured to not use ICU on Linux. 51 | */ 52 | bool StringSorter::ignorePunctation() const 53 | { 54 | return m_collator.ignorePunctuation(); 55 | } 56 | 57 | void StringSorter::setIgnorePunctation(bool ignorePunctation) 58 | { 59 | if (m_collator.ignorePunctuation() == ignorePunctation) 60 | return; 61 | 62 | m_collator.setIgnorePunctuation(ignorePunctation); 63 | Q_EMIT ignorePunctationChanged(); 64 | invalidate(); 65 | } 66 | 67 | /*! 68 | \qmlproperty Locale StringSorter::locale 69 | 70 | This property holds the locale of the sorter. 71 | */ 72 | QLocale StringSorter::locale() const 73 | { 74 | return m_collator.locale(); 75 | } 76 | 77 | void StringSorter::setLocale(const QLocale &locale) 78 | { 79 | if (m_collator.locale() == locale) 80 | return; 81 | 82 | m_collator.setLocale(locale); 83 | Q_EMIT localeChanged(); 84 | invalidate(); 85 | } 86 | 87 | /*! 88 | \qmlproperty bool StringSorter::numericMode 89 | 90 | This property holds whether the numeric mode of the sorter is enabled. 91 | This will enable proper sorting of numeric digits, so that e.g. 100 sorts after 99. 92 | By default this mode is off. 93 | */ 94 | bool StringSorter::numericMode() const 95 | { 96 | return m_collator.numericMode(); 97 | } 98 | 99 | void StringSorter::setNumericMode(bool numericMode) 100 | { 101 | if (m_collator.numericMode() == numericMode) 102 | return; 103 | 104 | m_collator.setNumericMode(numericMode); 105 | Q_EMIT numericModeChanged(); 106 | invalidate(); 107 | } 108 | 109 | int StringSorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 110 | { 111 | QPair pair = sourceData(sourceLeft, sourceRight, proxyModel); 112 | QString leftValue = pair.first.toString(); 113 | QString rightValue = pair.second.toString(); 114 | return m_collator.compare(leftValue, rightValue); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /tests/tst_helpers.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import SortFilterProxyModel.Test 0.2 6 | 7 | Item { 8 | ListModel { 9 | id: dataModel 10 | ListElement { 11 | firstName: "Tupac" 12 | lastName: "Shakur" 13 | } 14 | ListElement { 15 | firstName: "Charles" 16 | lastName: "Aznavour" 17 | } 18 | ListElement { 19 | firstName: "Frank" 20 | lastName: "Sinatra" 21 | } 22 | ListElement { 23 | firstName: "Laurent" 24 | lastName: "Garnier" 25 | } 26 | ListElement { 27 | firstName: "Phillipe" 28 | lastName: "Risoli" 29 | } 30 | } 31 | SortFilterProxyModel { 32 | id: testModel 33 | sourceModel: dataModel 34 | } 35 | SortFilterProxyModel { 36 | id: testModel2 37 | sourceModel: dataModel 38 | filters: ValueFilter { 39 | inverted: true 40 | roleName: "lastName" 41 | value: "Sinatra" 42 | } 43 | sorters: [ 44 | RoleSorter { roleName: "lastName"}, 45 | RoleSorter { roleName: "firstName"} 46 | ] 47 | } 48 | 49 | TestCase { 50 | name: "Helper functions" 51 | 52 | function test_getWithRoleName() { 53 | compare(testModel.get(0, "lastName"), "Shakur"); 54 | } 55 | 56 | function test_getWithoutRoleName() { 57 | compare(testModel.get(1), { firstName: "Charles", lastName: "Aznavour"}); 58 | } 59 | 60 | function test_roleForName() { 61 | compare(testModel.data(testModel.index(0, 0), testModel.roleForName("firstName")), "Tupac"); 62 | compare(testModel.data(testModel.index(1, 0), testModel.roleForName("lastName")), "Aznavour"); 63 | } 64 | 65 | function test_mapToSource() { 66 | compare(testModel2.mapToSource(3), 0); 67 | compare(testModel2.mapToSource(4), -1); 68 | } 69 | 70 | function test_mapToSourceLoop() { 71 | for (var i = 0; i < testModel2.count; ++i) { 72 | var sourceRow = testModel2.mapToSource(i); 73 | compare(testModel2.get(i).lastName, dataModel.get(sourceRow).lastName); 74 | } 75 | } 76 | 77 | function test_mapToSourceLoop_index() { 78 | for (var i = 0; i < testModel2.count; ++i) { 79 | var proxyIndex = testModel2.index(i, 0); 80 | var sourceIndex = testModel2.mapToSource(proxyIndex); 81 | var roleNumber = testModel2.roleForName("lastName"); 82 | compare(testModel2.data(proxyIndex, roleNumber), dataModel.data(sourceIndex, roleNumber)); 83 | } 84 | } 85 | 86 | function test_mapFromSource() { 87 | compare(testModel2.mapFromSource(1), 0); 88 | compare(testModel2.mapFromSource(2), -1); 89 | } 90 | 91 | function test_mapFromSourceLoop() { 92 | for (var i = 0; i < dataModel.count; ++i) { 93 | var proxyRow = testModel2.mapFromSource(i); 94 | if (proxyRow !== -1) { 95 | compare(dataModel.get(i).lastName, testModel2.get(proxyRow).lastName); 96 | } 97 | } 98 | } 99 | 100 | function test_mapFromSourceLoop_index() { 101 | for (var i = 0; i < dataModel.count; ++i) { 102 | var sourceIndex = dataModel.index(i, 0); 103 | var proxyIndex = testModel2.mapFromSource(sourceIndex); 104 | var roleNumber = testModel2.roleForName("lastName"); 105 | if (proxyIndex.valid) 106 | compare(testModel2.data(proxyIndex, roleNumber), dataModel.data(sourceIndex, roleNumber)); 107 | } 108 | } 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/tst_rangefilter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import SortFilterProxyModel 0.2 3 | import QtQml.Models 2.2 4 | import QtTest 1.1 5 | 6 | Item { 7 | property list filters: [ 8 | RangeFilter { 9 | property string tag: "inclusive" 10 | property int expectedModelCount: 3 11 | property var expectedValues: [3, 2, 4] 12 | property QtObject dataModel: dataModel0 13 | roleName: "value"; minimumValue: 2; maximumValue: 4 14 | }, 15 | RangeFilter { 16 | property string tag: "explicitInclusive" 17 | property int expectedModelCount: 3 18 | property var expectedValues: [3, 2, 4] 19 | property QtObject dataModel: dataModel0 20 | roleName: "value"; minimumValue: 2; maximumValue: 4; minimumInclusive: true; maximumInclusive: true 21 | }, 22 | RangeFilter { 23 | property string tag: "inclusiveMinExclusiveMax" 24 | property int expectedModelCount: 2 25 | property var expectedValues: [2, 3] 26 | property QtObject dataModel: dataModel1 27 | roleName: "value"; minimumValue: 2; maximumValue: 4; minimumInclusive: true; maximumInclusive: false 28 | }, 29 | RangeFilter { 30 | property string tag: "exclusiveMinInclusiveMax" 31 | property int expectedModelCount: 2 32 | property var expectedValues: [3, 4] 33 | property QtObject dataModel: dataModel1 34 | roleName: "value"; minimumValue: 2; maximumValue: 4; minimumInclusive: false; maximumInclusive: true 35 | }, 36 | RangeFilter { 37 | property string tag: "exclusive" 38 | property int expectedModelCount: 1 39 | property var expectedValues: [3] 40 | property QtObject dataModel: dataModel1 41 | roleName: "value"; minimumValue: 2; maximumValue: 4; minimumInclusive: false; maximumInclusive: false 42 | }, 43 | RangeFilter { 44 | property string tag: "outOfBoundsRange" 45 | property var expectedValues: [] 46 | property QtObject dataModel: dataModel1 47 | roleName: "value"; minimumValue: 4; maximumValue: 2 48 | }, 49 | RangeFilter { 50 | objectName: tag 51 | property string tag: "noMinimum" 52 | property var expectedValues: [3, 1, 2] 53 | property QtObject dataModel: dataModel0 54 | roleName: "value"; maximumValue: 3 55 | } 56 | ] 57 | 58 | ListModel { 59 | id: dataModel0 60 | ListElement { value: 5 } 61 | ListElement { value: 3 } 62 | ListElement { value: 1 } 63 | ListElement { value: 2 } 64 | ListElement { value: 4 } 65 | } 66 | 67 | ListModel { 68 | id: dataModel1 69 | ListElement { value: 5 } 70 | ListElement { value: 2 } 71 | ListElement { value: 3 } 72 | ListElement { value: 1 } 73 | ListElement { value: 4 } 74 | } 75 | 76 | SortFilterProxyModel { id: testModel } 77 | 78 | TestCase { 79 | name:"RangeFilterTests" 80 | 81 | function test_minMax_data() { 82 | return filters; 83 | } 84 | 85 | function test_minMax(filter) { 86 | testModel.sourceModel = filter.dataModel; 87 | testModel.filters = filter; 88 | 89 | verify(testModel.count === filter.expectedValues.length, 90 | "Expected count " + filter.expectedValues.length + ", actual count: " + testModel.count); 91 | for (var i = 0; i < testModel.count; i++) 92 | { 93 | var modelValue = testModel.get(i, filter.roleName); 94 | verify(modelValue === filter.expectedValues[i], 95 | "Expected testModel value " + filter.expectedValues[i] + ", actual: " + modelValue); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /sorters/sorter.cpp: -------------------------------------------------------------------------------- 1 | #include "sorter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype Sorter 8 | \qmlabstract 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Sorters 11 | \brief Base type for the \l SortFilterProxyModel sorters. 12 | 13 | The Sorter type cannot be used directly in a QML file. 14 | It exists to provide a set of common properties and methods, 15 | available across all the other sorters types that inherit from it. 16 | Attempting to use the Sorter type directly will result in an error. 17 | */ 18 | 19 | Sorter::Sorter(QObject *parent) : QObject(parent) 20 | { 21 | } 22 | 23 | Sorter::~Sorter() = default; 24 | 25 | /*! 26 | \qmlproperty bool Sorter::enabled 27 | 28 | This property holds whether the sorter is enabled. 29 | A disabled sorter will not change the order of the rows. 30 | 31 | By default, sorters are enabled. 32 | */ 33 | bool Sorter::enabled() const 34 | { 35 | return m_enabled; 36 | } 37 | 38 | void Sorter::setEnabled(bool enabled) 39 | { 40 | if (m_enabled == enabled) 41 | return; 42 | 43 | m_enabled = enabled; 44 | Q_EMIT enabledChanged(); 45 | Q_EMIT invalidated(); 46 | } 47 | 48 | bool Sorter::ascendingOrder() const 49 | { 50 | return sortOrder() == Qt::AscendingOrder; 51 | } 52 | 53 | void Sorter::setAscendingOrder(bool ascendingOrder) 54 | { 55 | setSortOrder(ascendingOrder ? Qt::AscendingOrder : Qt::DescendingOrder); 56 | } 57 | 58 | 59 | /*! 60 | \qmlproperty Qt::SortOrder Sorter::sortOrder 61 | 62 | This property holds the sort order of this sorter. 63 | 64 | \value Qt.AscendingOrder The items are sorted ascending e.g. starts with 'AAA' ends with 'ZZZ' in Latin-1 locales 65 | \value Qt.DescendingOrder The items are sorted descending e.g. starts with 'ZZZ' ends with 'AAA' in Latin-1 locales 66 | 67 | By default, sorting is in ascending order. 68 | */ 69 | Qt::SortOrder Sorter::sortOrder() const 70 | { 71 | return m_sortOrder; 72 | } 73 | 74 | void Sorter::setSortOrder(Qt::SortOrder sortOrder) 75 | { 76 | if (m_sortOrder == sortOrder) 77 | return; 78 | 79 | m_sortOrder = sortOrder; 80 | Q_EMIT sortOrderChanged(); 81 | invalidate(); 82 | } 83 | 84 | /*! 85 | \qmlproperty int Sorter::priority 86 | 87 | This property holds the sort priority of this sorter. 88 | Sorters with a higher priority are applied first. 89 | In case of equal priority, Sorters are ordered by their insertion order. 90 | 91 | By default, the priority is 0. 92 | */ 93 | int Sorter::priority() const 94 | { 95 | return m_priority; 96 | } 97 | 98 | void Sorter::setPriority(int priority) 99 | { 100 | if (m_priority == priority) 101 | return; 102 | 103 | m_priority = priority; 104 | Q_EMIT priorityChanged(); 105 | invalidate(); 106 | } 107 | 108 | int Sorter::compareRows(const QModelIndex &source_left, const QModelIndex &source_right, const QQmlSortFilterProxyModel& proxyModel) const 109 | { 110 | int comparison = compare(source_left, source_right, proxyModel); 111 | return (m_sortOrder == Qt::AscendingOrder) ? comparison : -comparison; 112 | } 113 | 114 | int Sorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 115 | { 116 | if (lessThan(sourceLeft, sourceRight, proxyModel)) 117 | return -1; 118 | if (lessThan(sourceRight, sourceLeft, proxyModel)) 119 | return 1; 120 | return 0; 121 | } 122 | 123 | void Sorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 124 | { 125 | Q_UNUSED(proxyModel) 126 | } 127 | 128 | bool Sorter::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 129 | { 130 | Q_UNUSED(sourceLeft) 131 | Q_UNUSED(sourceRight) 132 | Q_UNUSED(proxyModel) 133 | return false; 134 | } 135 | 136 | void Sorter::invalidate() 137 | { 138 | if (m_enabled) 139 | Q_EMIT invalidated(); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-filterrole.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FilterRole QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

FilterRole QML Type

21 | 22 | 23 |

A role resolving to true for rows matching all its filters. More...

24 | 25 |
26 |
Import Statement: import SortFilterProxyModel .
Inherits:

SingleRole

27 |
30 | 31 |

Properties

32 | 36 | 37 | 38 |

Detailed Description

39 |

A FilterRole is a ProxyRole that returns true for rows matching all its filters.

40 |

In the following example, the isAdult role will be equal to true if the age role is superior or equal to 18.

41 |
SortFilterProxyModel {
42 |     sourceModel: personModel
43 |     proxyRoles: FilterRole {
44 |         name: "isAdult"
45 |         RangeFilter { roleName: "age"; minimumValue: 18; minimumInclusive: true }
46 |     }
47 | }
48 |

See also FilterContainer.

49 | 50 |

Property Documentation

51 | 52 |
53 |
54 | 55 | 57 |

56 | [default] filters : list<Filter>

58 |

This property holds the list of filters for this filter role. The data of this role will be equal to the true if all its filters match the model row, false otherwise.

59 |

See also Filter and FilterContainer.

60 |
61 |
62 | 63 |
64 |
65 | 66 | 68 |

67 | name : string

69 |

This property holds the role name of the proxy role.

70 |
71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /tests/tst_proxyroles.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | import QtTest 1.1 4 | import SortFilterProxyModel 0.2 5 | import SortFilterProxyModel.Test 0.2 6 | import QtQml 2.2 7 | 8 | Item { 9 | ListModel { 10 | id: listModel 11 | ListElement { test: "first"; keep: true } 12 | ListElement { test: "second"; keep: true } 13 | ListElement { test: "third"; keep: true } 14 | } 15 | 16 | SortFilterProxyModel { 17 | id: testModel 18 | sourceModel: listModel 19 | filters: [ 20 | ValueFilter { 21 | roleName: "keep" 22 | value: true 23 | }, 24 | ValueFilter { 25 | inverted: true 26 | roleName: "staticRole" 27 | value: "filterMe" 28 | } 29 | ] 30 | 31 | proxyRoles: [ 32 | StaticRole { 33 | id: staticRole 34 | name: "staticRole" 35 | value: "foo" 36 | }, 37 | StaticRole { 38 | id: renameRole 39 | name: "renameMe" 40 | value: "test" 41 | }, 42 | SourceIndexRole { 43 | name: "sourceIndexRole" 44 | }, 45 | MultiRole {} 46 | ] 47 | } 48 | 49 | Instantiator { 50 | id: instantiator 51 | model: testModel 52 | QtObject { 53 | property string staticRole: model.staticRole 54 | property int sourceIndexRole: model.sourceIndexRole 55 | } 56 | } 57 | 58 | ListModel { 59 | id: singleRowModel 60 | ListElement { 61 | changingRole: "Change me" 62 | otherRole: "I don't change" 63 | } 64 | } 65 | 66 | SortFilterProxyModel { 67 | id: noProxyRolesProxyModel 68 | sourceModel: singleRowModel 69 | } 70 | 71 | Instantiator { 72 | id: outerInstantiator 73 | model: noProxyRolesProxyModel 74 | QtObject { 75 | property var counter: ({ count : 0 }) 76 | property string changingRole: model.changingRole 77 | property string otherRole: { 78 | ++counter.count; 79 | return model.otherRole; 80 | } 81 | } 82 | } 83 | 84 | TestCase { 85 | name: "ProxyRoles" 86 | 87 | function test_resetAfterNameChange() { 88 | var oldObject = instantiator.object; 89 | renameRole.name = "foobarRole"; 90 | var newObject = instantiator.object; 91 | verify(newObject !== oldObject, "Instantiator object should have been reinstantiated"); 92 | } 93 | 94 | function test_proxyRoleInvalidation() { 95 | compare(instantiator.object.staticRole, "foo"); 96 | staticRole.value = "bar"; 97 | compare(instantiator.object.staticRole, "bar"); 98 | } 99 | 100 | function test_proxyRoleGetDataFromSource() { 101 | compare(instantiator.object.sourceIndexRole, 0); 102 | compare(testModel.get(1, "sourceIndexRole"), 1); 103 | listModel.setProperty(1, "keep", false); 104 | compare(testModel.get(1, "sourceIndexRole"), 2); 105 | } 106 | 107 | function test_filterFromProxyRole() { 108 | staticRole.value = "filterMe"; 109 | compare(testModel.count, 0); 110 | staticRole.value = "foo"; 111 | compare(testModel.count, 3); 112 | } 113 | 114 | function test_multiRole() { 115 | compare(testModel.get(0, "role1"), "data for role1"); 116 | compare(testModel.get(0, "role2"), "data for role2"); 117 | } 118 | 119 | function test_ProxyRolesDataChanged() { 120 | outerInstantiator.object.counter.count = 0; 121 | singleRowModel.setProperty(0, "changingRole", "Changed") 122 | compare(outerInstantiator.object.changingRole, "Changed"); 123 | compare(outerInstantiator.object.counter.count, 0); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /filters/filtercontainer.cpp: -------------------------------------------------------------------------------- 1 | #include "filtercontainer.h" 2 | #include "filter.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype FilterContainer 9 | \qmlabstract 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup FilterAttached 12 | \brief Abstract interface for types containing \l {Filter}{Filters}. 13 | 14 | \section2 Types implementing this interface: 15 | \annotatedlist FilterContainer 16 | */ 17 | 18 | QList FilterContainer::filters() const 19 | { 20 | return m_filters; 21 | } 22 | 23 | void FilterContainer::appendFilter(Filter* filter) 24 | { 25 | m_filters.append(filter); 26 | onFilterAppended(filter); 27 | } 28 | 29 | void FilterContainer::removeFilter(Filter* filter) 30 | { 31 | m_filters.removeOne(filter); 32 | onFilterRemoved(filter); 33 | } 34 | 35 | void FilterContainer::clearFilters() 36 | { 37 | m_filters.clear(); 38 | onFiltersCleared(); 39 | } 40 | 41 | QQmlListProperty FilterContainer::filtersListProperty() 42 | { 43 | return QQmlListProperty(reinterpret_cast(this), &m_filters, 44 | &FilterContainer::append_filter, 45 | &FilterContainer::count_filter, 46 | &FilterContainer::at_filter, 47 | &FilterContainer::clear_filters); 48 | } 49 | 50 | void FilterContainer::append_filter(QQmlListProperty* list, Filter* filter) 51 | { 52 | if (!filter) 53 | return; 54 | 55 | FilterContainer* that = reinterpret_cast(list->object); 56 | that->appendFilter(filter); 57 | } 58 | 59 | qqsfpm::FilterContainer::sizetype FilterContainer::count_filter(QQmlListProperty* list) 60 | { 61 | QList* filters = static_cast*>(list->data); 62 | return filters->count(); 63 | } 64 | 65 | Filter* FilterContainer::at_filter(QQmlListProperty* list, qqsfpm::FilterContainer::sizetype index) 66 | { 67 | QList* filters = static_cast*>(list->data); 68 | return filters->at(index); 69 | } 70 | 71 | void FilterContainer::clear_filters(QQmlListProperty *list) 72 | { 73 | FilterContainer* that = reinterpret_cast(list->object); 74 | that->clearFilters(); 75 | } 76 | 77 | FilterContainerAttached::FilterContainerAttached(QObject* object) : QObject(object), 78 | m_filter(qobject_cast(object)) 79 | { 80 | if (!m_filter) 81 | qmlWarning(object) << "FilterContainer must be attached to a Filter"; 82 | } 83 | 84 | FilterContainerAttached::~FilterContainerAttached() 85 | { 86 | if (m_filter && m_container) { 87 | FilterContainer* container = qobject_cast(m_container.data()); 88 | container->removeFilter(m_filter); 89 | } 90 | } 91 | 92 | /*! 93 | \qmlattachedproperty bool FilterContainer::container 94 | This attached property allows you to include in a \l FilterContainer a \l Filter that 95 | has been instantiated outside of the \l FilterContainer, for example in an Instantiator. 96 | */ 97 | QObject* FilterContainerAttached::container() const 98 | { 99 | return m_container; 100 | } 101 | 102 | void FilterContainerAttached::setContainer(QObject* object) 103 | { 104 | if (m_container == object) 105 | return; 106 | 107 | FilterContainer* container = qobject_cast(object); 108 | if (object && !container) 109 | qmlWarning(parent()) << "container must inherits from FilterContainer, " << object->metaObject()->className() << " provided"; 110 | 111 | if (m_container && m_filter) 112 | qobject_cast(m_container.data())->removeFilter(m_filter); 113 | 114 | m_container = container ? object : nullptr; 115 | if (container && m_filter) 116 | container->appendFilter(m_filter); 117 | 118 | Q_EMIT containerChanged(); 119 | } 120 | 121 | FilterContainerAttached* FilterContainerAttached::qmlAttachedProperties(QObject* object) 122 | { 123 | return new FilterContainerAttached(object); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /sorters/sortercontainer.cpp: -------------------------------------------------------------------------------- 1 | #include "sortercontainer.h" 2 | #include "sorter.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype SorterContainer 9 | \qmlabstract 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup SorterAttached 12 | \brief Abstract interface for types containing \l {Sorter}{Sorters}. 13 | 14 | \section2 Types implementing this interface: 15 | \annotatedlist SorterContainer 16 | */ 17 | 18 | QList SorterContainer::sorters() const 19 | { 20 | return m_sorters; 21 | } 22 | 23 | void SorterContainer::appendSorter(Sorter* sorter) 24 | { 25 | m_sorters.append(sorter); 26 | onSorterAppended(sorter); 27 | } 28 | 29 | void SorterContainer::removeSorter(Sorter *sorter) 30 | { 31 | m_sorters.removeOne(sorter); 32 | onSorterRemoved(sorter); 33 | } 34 | 35 | void SorterContainer::clearSorters() 36 | { 37 | m_sorters.clear(); 38 | onSortersCleared(); 39 | } 40 | 41 | QQmlListProperty SorterContainer::sortersListProperty() 42 | { 43 | return QQmlListProperty(reinterpret_cast(this), &m_sorters, 44 | &SorterContainer::append_sorter, 45 | &SorterContainer::count_sorter, 46 | &SorterContainer::at_sorter, 47 | &SorterContainer::clear_sorters); 48 | } 49 | 50 | void SorterContainer::append_sorter(QQmlListProperty* list, Sorter* sorter) 51 | { 52 | if (!sorter) 53 | return; 54 | 55 | SorterContainer* that = reinterpret_cast(list->object); 56 | that->appendSorter(sorter); 57 | } 58 | 59 | SorterContainer::sizetype SorterContainer::count_sorter(QQmlListProperty* list) 60 | { 61 | QList* sorters = static_cast*>(list->data); 62 | return sorters->count(); 63 | } 64 | 65 | Sorter* SorterContainer::at_sorter(QQmlListProperty* list, qqsfpm::SorterContainer::sizetype index) 66 | { 67 | QList* sorters = static_cast*>(list->data); 68 | return sorters->at(index); 69 | } 70 | 71 | void SorterContainer::clear_sorters(QQmlListProperty *list) 72 | { 73 | SorterContainer* that = reinterpret_cast(list->object); 74 | that->clearSorters(); 75 | } 76 | 77 | SorterContainerAttached::SorterContainerAttached(QObject* object) : QObject(object), 78 | m_sorter(qobject_cast(object)) 79 | { 80 | if (!m_sorter) 81 | qmlWarning(object) << "SorterContainerAttached must be attached to a Sorter"; 82 | } 83 | 84 | SorterContainerAttached::~SorterContainerAttached() 85 | { 86 | if (m_sorter && m_container) { 87 | SorterContainer* container = qobject_cast(m_container.data()); 88 | container->removeSorter(m_sorter); 89 | } 90 | } 91 | 92 | /*! 93 | \qmlattachedproperty bool SorterContainer::container 94 | This attached property allows you to include in a \l SorterContainer a \l Sorter that 95 | has been instantiated outside of the \l SorterContainer, for example in an Instantiator. 96 | */ 97 | QObject* SorterContainerAttached::container() const 98 | { 99 | return m_container; 100 | } 101 | 102 | void SorterContainerAttached::setContainer(QObject* object) 103 | { 104 | if (m_container == object) 105 | return; 106 | 107 | SorterContainer* container = qobject_cast(object); 108 | if (object && !container) 109 | qmlWarning(parent()) << "container must inherits from SorterContainer, " << object->metaObject()->className() << " provided"; 110 | 111 | if (m_container && m_sorter) 112 | qobject_cast(m_container.data())->removeSorter(m_sorter); 113 | 114 | m_container = container ? object : nullptr; 115 | if (container && m_sorter) 116 | container->appendSorter(m_sorter); 117 | 118 | Q_EMIT containerChanged(); 119 | } 120 | 121 | SorterContainerAttached* SorterContainerAttached::qmlAttachedProperties(QObject* object) 122 | { 123 | return new SorterContainerAttached(object); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /filters/rangefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "rangefilter.h" 2 | 3 | #include "qvariantlessthan.h" 4 | 5 | 6 | 7 | 8 | namespace qqsfpm { 9 | 10 | /*! 11 | \qmltype RangeFilter 12 | \inherits RoleFilter 13 | \inqmlmodule SortFilterProxyModel 14 | \ingroup Filters 15 | \brief Filters rows between boundary values. 16 | 17 | A RangeFilter is a \l RoleFilter that accepts rows if their data is between the filter's minimum and maximum value. 18 | 19 | In the following example, only rows with their \c price role set to a value between the tow boundary of the slider will be accepted : 20 | \code 21 | RangeSlider { 22 | id: priceRangeSlider 23 | } 24 | 25 | SortFilterProxyModel { 26 | sourceModel: priceModel 27 | filters: RangeFilter { 28 | roleName: "price" 29 | minimumValue: priceRangeSlider.first.value 30 | maximumValue: priceRangeSlider.second.value 31 | } 32 | } 33 | \endcode 34 | */ 35 | 36 | /*! 37 | \qmlproperty int RangeFilter::minimumValue 38 | 39 | This property holds the minimumValue of the filter. 40 | Rows with a value lower than \c minimumValue will be rejected. 41 | 42 | By default, no value is set. 43 | 44 | \sa minimumInclusive 45 | */ 46 | QVariant RangeFilter::minimumValue() const 47 | { 48 | return m_minimumValue; 49 | } 50 | 51 | void RangeFilter::setMinimumValue(QVariant minimumValue) 52 | { 53 | if (m_minimumValue == minimumValue) 54 | return; 55 | 56 | m_minimumValue = minimumValue; 57 | Q_EMIT minimumValueChanged(); 58 | invalidate(); 59 | } 60 | 61 | /*! 62 | \qmlproperty int RangeFilter::minimumInclusive 63 | 64 | This property holds whether the \l minimumValue is inclusive. 65 | 66 | By default, the \l minimumValue is inclusive. 67 | 68 | \sa minimumValue 69 | */ 70 | bool RangeFilter::minimumInclusive() const 71 | { 72 | return m_minimumInclusive; 73 | } 74 | 75 | void RangeFilter::setMinimumInclusive(bool minimumInclusive) 76 | { 77 | if (m_minimumInclusive == minimumInclusive) 78 | return; 79 | 80 | m_minimumInclusive = minimumInclusive; 81 | Q_EMIT minimumInclusiveChanged(); 82 | invalidate(); 83 | } 84 | 85 | /*! 86 | \qmlproperty int RangeFilter::maximumValue 87 | 88 | This property holds the maximumValue of the filter. 89 | Rows with a value higher than \c maximumValue will be rejected. 90 | 91 | By default, no value is set. 92 | 93 | \sa maximumInclusive 94 | */ 95 | QVariant RangeFilter::maximumValue() const 96 | { 97 | return m_maximumValue; 98 | } 99 | 100 | void RangeFilter::setMaximumValue(QVariant maximumValue) 101 | { 102 | if (m_maximumValue == maximumValue) 103 | return; 104 | 105 | m_maximumValue = maximumValue; 106 | Q_EMIT maximumValueChanged(); 107 | invalidate(); 108 | } 109 | 110 | /*! 111 | \qmlproperty int RangeFilter::maximumInclusive 112 | 113 | This property holds whether the \l minimumValue is inclusive. 114 | 115 | By default, the \l minimumValue is inclusive. 116 | 117 | \sa minimumValue 118 | */ 119 | bool RangeFilter::maximumInclusive() const 120 | { 121 | return m_maximumInclusive; 122 | } 123 | 124 | void RangeFilter::setMaximumInclusive(bool maximumInclusive) 125 | { 126 | if (m_maximumInclusive == maximumInclusive) 127 | return; 128 | 129 | m_maximumInclusive = maximumInclusive; 130 | Q_EMIT maximumInclusiveChanged(); 131 | invalidate(); 132 | } 133 | 134 | bool RangeFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 135 | { 136 | const QVariant value = sourceData(sourceIndex, proxyModel); 137 | bool lessThanMin = m_minimumValue.isValid() && 138 | (m_minimumInclusive ? qqsfpm::lessThan(value, m_minimumValue) 139 | : !qqsfpm::lessThan(m_minimumValue, value)); 140 | bool moreThanMax = m_maximumValue.isValid() && 141 | (m_maximumInclusive ? qqsfpm::lessThan(m_maximumValue, value) 142 | : !qqsfpm::lessThan(value, m_maximumValue)); 143 | return !(lessThanMin || moreThanMax); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-filtercontainer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FilterContainer QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 21 |

FilterContainer QML Type

22 | 23 | 24 |

Abstract interface for types containing Filters. More...

25 | 26 |
27 |
Import Statement: import SortFilterProxyModel .
30 | 31 |

Attached Properties

32 | 35 | 36 | 37 |

Detailed Description

38 | 39 |

Types implementing this interface:

40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 |

AllOf

Filter container accepting rows accepted by all its child filters

AnyOf

Filter container accepting rows accepted by at least one of its child filters

FilterRole

A role resolving to true for rows matching all its filters

FilterSorter

Sorts rows based on if they match filters

SortFilterProxyModel

Filters and sorts data coming from a source QAbstractItemModel

SwitchRole

A role using Filter to conditionnaly compute its data

48 | 49 |

Attached Property Documentation

50 | 51 |
52 |
53 | 54 | 56 |

55 | FilterContainer.container : bool

57 |

This attached property allows you to include in a FilterContainer a Filter that has been instantiated outside of the FilterContainer, for example in an Instantiator.

58 |
59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-anyof.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AnyOf QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

AnyOf QML Type

21 | 22 | 23 |

Filter container accepting rows accepted by at least one of its child filters. More...

24 | 25 |
26 |
Import Statement: import SortFilterProxyModel .
Inherits:

Filter

27 |
30 | 31 |

Properties

32 | 36 | 37 | 38 |

Detailed Description

39 |

The AnyOf type is a Filter container that accepts rows if any of its contained (and enabled) filters accept them.

40 |

In the following example, only the rows where the firstName role or the lastName role match the text entered in the nameTextField will be accepted :

41 |
TextField {
42 |   id: nameTextField
43 | }
44 | 
45 | SortFilterProxyModel {
46 |   sourceModel: contactModel
47 |   filters: AnyOf {
48 |       RegExpFilter {
49 |           roleName: "lastName"
50 |           pattern: nameTextField.text
51 |           caseSensitivity: Qt.CaseInsensitive
52 |       }
53 |       RegExpFilter {
54 |           roleName: "firstName"
55 |           pattern: nameTextField.text
56 |           caseSensitivity: Qt.CaseInsensitive
57 |       }
58 |   }
59 | }
60 |

See also FilterContainer.

61 | 62 |

Property Documentation

63 | 64 |
65 |
66 | 67 | 69 |

68 | enabled : bool

70 |

This property holds whether the filter is enabled. A disabled filter will accept every rows unconditionally (even if it's inverted).

71 |

By default, filters are enabled.

72 |
73 |
74 | 75 |
76 |
77 | 78 | 80 |

79 | inverted : bool

81 |

This property holds whether the filter is inverted. When a filter is inverted, a row normally accepted would be rejected, and vice-versa.

82 |

By default, filters are not inverted.

83 |
84 |
85 | 86 | 87 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-joinrole.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | JoinRole QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

JoinRole QML Type

21 | 22 | 23 |

a role made from concatenating other roles. More...

24 | 25 |
26 |
Import Statement: import SortFilterProxyModel .
Inherits:

SingleRole

27 |
30 | 31 |

Properties

32 | 37 | 38 | 39 |

Detailed Description

40 |

A JoinRole is a simple ProxyRole that concatenates other roles.

41 |

In the following example, the fullName role is computed by the concatenation of the firstName role and the lastName role separated by a space :

42 |
SortFilterProxyModel {
43 |    sourceModel: contactModel
44 |    proxyRoles: JoinRole {
45 |        name: "fullName"
46 |        roleNames: ["firstName", "lastName"]
47 |   }
48 | }
49 | 50 |

Property Documentation

51 | 52 |
53 |
54 | 55 | 57 |

56 | name : string

58 |

This property holds the role name of the proxy role.

59 |
60 |
61 | 62 |
63 |
64 | 65 | 67 |

66 | roleNames : list<string>

68 |

This property holds the role names that are joined by this role.

69 |
70 |
71 | 72 |
73 |
74 | 75 | 77 |

76 | separator : string

78 |

This property holds the separator that is used to join the roles specified in roleNames.

79 |

By default, it's a space.

80 |
81 |
82 | 83 | 84 | --------------------------------------------------------------------------------