├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── SortFilterProxyModel.pri ├── SortFilterProxyModel.qbs ├── docs ├── index.html ├── qml-sortfilterproxymodel-allof-members.html ├── qml-sortfilterproxymodel-allof.html ├── qml-sortfilterproxymodel-anyof-members.html ├── qml-sortfilterproxymodel-anyof.html ├── qml-sortfilterproxymodel-expressionfilter-members.html ├── qml-sortfilterproxymodel-expressionfilter.html ├── qml-sortfilterproxymodel-expressionrole-members.html ├── qml-sortfilterproxymodel-expressionrole.html ├── qml-sortfilterproxymodel-expressionsorter-members.html ├── qml-sortfilterproxymodel-expressionsorter.html ├── qml-sortfilterproxymodel-filter-members.html ├── qml-sortfilterproxymodel-filter.html ├── qml-sortfilterproxymodel-filtercontainer-members.html ├── qml-sortfilterproxymodel-filtercontainer.html ├── qml-sortfilterproxymodel-filterrole-members.html ├── qml-sortfilterproxymodel-filterrole.html ├── qml-sortfilterproxymodel-filtersorter-members.html ├── qml-sortfilterproxymodel-filtersorter.html ├── qml-sortfilterproxymodel-indexfilter-members.html ├── qml-sortfilterproxymodel-indexfilter.html ├── qml-sortfilterproxymodel-joinrole-members.html ├── qml-sortfilterproxymodel-joinrole.html ├── qml-sortfilterproxymodel-proxyrole-members.html ├── qml-sortfilterproxymodel-proxyrole.html ├── qml-sortfilterproxymodel-rangefilter-members.html ├── qml-sortfilterproxymodel-rangefilter.html ├── qml-sortfilterproxymodel-regexpfilter-members.html ├── qml-sortfilterproxymodel-regexpfilter.html ├── qml-sortfilterproxymodel-regexprole-members.html ├── qml-sortfilterproxymodel-regexprole.html ├── qml-sortfilterproxymodel-rolefilter-members.html ├── qml-sortfilterproxymodel-rolefilter.html ├── qml-sortfilterproxymodel-rolesorter-members.html ├── qml-sortfilterproxymodel-rolesorter.html ├── qml-sortfilterproxymodel-singlerole-members.html ├── qml-sortfilterproxymodel-singlerole.html ├── qml-sortfilterproxymodel-sorter-members.html ├── qml-sortfilterproxymodel-sorter.html ├── qml-sortfilterproxymodel-sortercontainer-members.html ├── qml-sortfilterproxymodel-sortercontainer.html ├── qml-sortfilterproxymodel-sortfilterproxymodel-members.html ├── qml-sortfilterproxymodel-sortfilterproxymodel.html ├── qml-sortfilterproxymodel-stringsorter-members.html ├── qml-sortfilterproxymodel-stringsorter.html ├── qml-sortfilterproxymodel-switchrole-members.html ├── qml-sortfilterproxymodel-switchrole.html ├── qml-sortfilterproxymodel-valuefilter-members.html ├── qml-sortfilterproxymodel-valuefilter.html ├── sortfilterproxymodel-qmlmodule.html ├── sortfilterproxymodel.index └── style │ └── offline.css ├── filters ├── alloffilter.cpp ├── alloffilter.h ├── anyoffilter.cpp ├── anyoffilter.h ├── expressionfilter.cpp ├── expressionfilter.h ├── filter.cpp ├── filter.h ├── filtercontainer.cpp ├── filtercontainer.h ├── filtercontainerfilter.cpp ├── filtercontainerfilter.h ├── filtersqmltypes.cpp ├── indexfilter.cpp ├── indexfilter.h ├── rangefilter.cpp ├── rangefilter.h ├── regexpfilter.cpp ├── regexpfilter.h ├── rolefilter.cpp ├── rolefilter.h ├── valuefilter.cpp └── valuefilter.h ├── index.qdoc ├── offline.css ├── proxyroles ├── expressionrole.cpp ├── expressionrole.h ├── filterrole.cpp ├── filterrole.h ├── joinrole.cpp ├── joinrole.h ├── proxyrole.cpp ├── proxyrole.h ├── proxyrolecontainer.cpp ├── proxyrolecontainer.h ├── proxyrolesqmltypes.cpp ├── regexprole.cpp ├── regexprole.h ├── singlerole.cpp ├── singlerole.h ├── switchrole.cpp └── switchrole.h ├── qpm.json ├── qqmlsortfilterproxymodel.cpp ├── qqmlsortfilterproxymodel.h ├── sorters ├── expressionsorter.cpp ├── expressionsorter.h ├── filtersorter.cpp ├── filtersorter.h ├── rolesorter.cpp ├── rolesorter.h ├── sorter.cpp ├── sorter.h ├── sortercontainer.cpp ├── sortercontainer.h ├── sortersqmltypes.cpp ├── stringsorter.cpp └── stringsorter.h ├── sortfilterproxymodel.qdocconf └── tests ├── SortFilterProxyModel.pro ├── indexsorter.cpp ├── indexsorter.h ├── testroles.cpp ├── testroles.h ├── tst_builtins.qml ├── tst_delayed.qml ├── tst_expressionrole.qml ├── tst_filtercontainerattached.qml ├── tst_filtercontainers.qml ├── tst_filterrole.qml ├── tst_filtersorter.qml ├── tst_helpers.qml ├── tst_indexfilter.qml ├── tst_joinrole.qml ├── tst_proxyroles.qml ├── tst_rangefilter.qml ├── tst_regexprole.qml ├── tst_rolesorter.qml ├── tst_sortercontainerattached.qml ├── tst_sorters.qml ├── tst_sortfilterproxymodel.cpp ├── tst_sourceroles.qml ├── tst_stringsorter.qml └── tst_switchrole.qml /.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 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | set(CMAKE_CXX_STANDARD 11) 4 | 5 | find_package(Qt5 REQUIRED 6 | Core 7 | Qml 8 | ) 9 | 10 | set(CMAKE_AUTOMOC ON) 11 | set(CMAKE_INCLUDE_CURRENT_DIR ON) # This is to find generated *.moc and *.h files in build dir 12 | 13 | add_library(SortFilterProxyModel OBJECT 14 | qqmlsortfilterproxymodel.cpp 15 | filters/filter.cpp 16 | filters/filtercontainer.cpp 17 | filters/rolefilter.cpp 18 | filters/valuefilter.cpp 19 | filters/indexfilter.cpp 20 | filters/regexpfilter.cpp 21 | filters/rangefilter.cpp 22 | filters/expressionfilter.cpp 23 | filters/filtercontainerfilter.cpp 24 | filters/anyoffilter.cpp 25 | filters/alloffilter.cpp 26 | filters/filtersqmltypes.cpp 27 | sorters/sorter.cpp 28 | sorters/sortercontainer.cpp 29 | sorters/rolesorter.cpp 30 | sorters/stringsorter.cpp 31 | sorters/expressionsorter.cpp 32 | sorters/sortersqmltypes.cpp 33 | proxyroles/proxyrole.cpp 34 | proxyroles/proxyrolecontainer.cpp 35 | proxyroles/joinrole.cpp 36 | proxyroles/switchrole.cpp 37 | proxyroles/expressionrole.cpp 38 | proxyroles/proxyrolesqmltypes.cpp 39 | proxyroles/singlerole.cpp 40 | proxyroles/regexprole.cpp 41 | sorters/filtersorter.cpp 42 | proxyroles/filterrole.cpp 43 | ) 44 | 45 | target_include_directories(SortFilterProxyModel PUBLIC 46 | ${CMAKE_CURRENT_LIST_DIR} 47 | $ 48 | $ 49 | ) 50 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /SortFilterProxyModel.pri: -------------------------------------------------------------------------------- 1 | !contains( CONFIG, c\+\+1[14] ): 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/filters/filter.h \ 7 | $$PWD/filters/filtercontainer.h \ 8 | $$PWD/filters/rolefilter.h \ 9 | $$PWD/filters/valuefilter.h \ 10 | $$PWD/filters/indexfilter.h \ 11 | $$PWD/filters/regexpfilter.h \ 12 | $$PWD/filters/rangefilter.h \ 13 | $$PWD/filters/expressionfilter.h \ 14 | $$PWD/filters/filtercontainerfilter.h \ 15 | $$PWD/filters/anyoffilter.h \ 16 | $$PWD/filters/alloffilter.h \ 17 | $$PWD/sorters/sorter.h \ 18 | $$PWD/sorters/sortercontainer.h \ 19 | $$PWD/sorters/rolesorter.h \ 20 | $$PWD/sorters/stringsorter.h \ 21 | $$PWD/sorters/expressionsorter.h \ 22 | $$PWD/proxyroles/proxyrole.h \ 23 | $$PWD/proxyroles/proxyrolecontainer.h \ 24 | $$PWD/proxyroles/joinrole.h \ 25 | $$PWD/proxyroles/switchrole.h \ 26 | $$PWD/proxyroles/expressionrole.h \ 27 | $$PWD/proxyroles/singlerole.h \ 28 | $$PWD/proxyroles/regexprole.h \ 29 | $$PWD/sorters/filtersorter.h \ 30 | $$PWD/proxyroles/filterrole.h 31 | 32 | SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \ 33 | $$PWD/filters/filter.cpp \ 34 | $$PWD/filters/filtercontainer.cpp \ 35 | $$PWD/filters/rolefilter.cpp \ 36 | $$PWD/filters/valuefilter.cpp \ 37 | $$PWD/filters/indexfilter.cpp \ 38 | $$PWD/filters/regexpfilter.cpp \ 39 | $$PWD/filters/rangefilter.cpp \ 40 | $$PWD/filters/expressionfilter.cpp \ 41 | $$PWD/filters/filtercontainerfilter.cpp \ 42 | $$PWD/filters/anyoffilter.cpp \ 43 | $$PWD/filters/alloffilter.cpp \ 44 | $$PWD/filters/filtersqmltypes.cpp \ 45 | $$PWD/sorters/sorter.cpp \ 46 | $$PWD/sorters/sortercontainer.cpp \ 47 | $$PWD/sorters/rolesorter.cpp \ 48 | $$PWD/sorters/stringsorter.cpp \ 49 | $$PWD/sorters/expressionsorter.cpp \ 50 | $$PWD/sorters/sortersqmltypes.cpp \ 51 | $$PWD/proxyroles/proxyrole.cpp \ 52 | $$PWD/proxyroles/proxyrolecontainer.cpp \ 53 | $$PWD/proxyroles/joinrole.cpp \ 54 | $$PWD/proxyroles/switchrole.cpp \ 55 | $$PWD/proxyroles/expressionrole.cpp \ 56 | $$PWD/proxyroles/proxyrolesqmltypes.cpp \ 57 | $$PWD/proxyroles/singlerole.cpp \ 58 | $$PWD/proxyroles/regexprole.cpp \ 59 | $$PWD/sorters/filtersorter.cpp \ 60 | $$PWD/proxyroles/filterrole.cpp 61 | -------------------------------------------------------------------------------- /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 | "qqmlsortfilterproxymodel.cpp", 61 | "qqmlsortfilterproxymodel.h" 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /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-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-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-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-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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-expressionrole.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ExpressionRole QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

ExpressionRole QML Type

21 | 22 | 23 |

A custom role computed from a javascript expression. More...

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

SingleRole

27 |
30 | 31 |

Properties

32 | 36 | 37 | 38 |

Detailed Description

39 |

An ExpressionRole is a ProxyRole allowing to implement a custom role based on a javascript expression.

40 |

In the following example, the c role is computed by adding the a role and b role of the model :

41 |
SortFilterProxyModel {
42 |    sourceModel: numberModel
43 |    proxyRoles: ExpressionRole {
44 |        name: "c"
45 |        expression: model.a + model.b
46 |   }
47 | }
48 | 49 |

Property Documentation

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

55 | expression : expression

57 |

An expression to implement a custom role. It has the same syntax has a Property Binding except it will be evaluated for each of the source model's rows. The data for this role will be the retuned valued of the expression. Data for each row is exposed like for a delegate of a QML View.

58 |

This expression is reevaluated for a row every time its model data changes. When an external property (not index or in model) the expression depends on changes, the expression is reevaluated for every row of the source model. To capture the properties the expression depends on, the expression is first executed with invalid data and each property access is detected by the QML engine. This means that if a property is not accessed because of a conditional, it won't be captured and the expression won't be reevaluted when this property changes.

59 |

A workaround to this problem is to access all the properties the expressions depends unconditionally at the beggining of the expression.

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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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-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 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-regexprole.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | RegExpRole QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

RegExpRole QML Type

21 | 22 | 23 |

A ProxyRole extracting data from a source role via a regular expression. More...

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

ProxyRole

27 |
30 | 31 |

Properties

32 | 37 | 38 | 39 |

Detailed Description

40 |

A RegExpRole is a ProxyRole that provides a role for each named capture group of its regular expression pattern.

41 |

In the following example, the date role of the source model will be extracted in 3 roles in the proxy moodel: year, month and day.

42 |
SortFilterProxyModel {
43 |     sourceModel: eventModel
44 |     proxyRoles: RegExpRole {
45 |         roleName: "date"
46 |         pattern: "(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})"
47 |     }
48 | }
49 | 50 |

Property Documentation

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

56 | caseSensitivity : Qt::CaseSensitivity

58 |

This property holds the caseSensitivity of the regular expression.

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

66 | pattern : QString

68 |

This property holds the pattern of the regular expression of this RegExpRole. The RegExpRole will expose a role for each of the named capture group of the pattern.

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

76 | roleName : QString

78 |

This property holds the role name that the RegExpRole is using to query the source model's data to extract new roles from.

79 |
80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/qml-sortfilterproxymodel-rolefilter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | RoleFilter QML Type | SortFilterProxyModel 8 | 9 | 10 | 11 | 20 |

RoleFilter QML Type

21 | 22 | 23 |

Base type for filters based on a source model role. More...

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

Filter

27 |
Inherited By:

RangeFilter, RegExpFilter, and ValueFilter

28 |
31 | 32 |

Properties

33 | 38 | 39 | 40 |

Detailed Description

41 |

The RoleFilter 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 RoleFilter type directly will result in an error.

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 |
69 | 70 | 72 |

71 | roleName : string

73 |

This property holds the role name that the filter is using to query the source model's data when filtering items.

74 |
75 |
76 | 77 | 78 | -------------------------------------------------------------------------------- /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-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 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /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 | 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-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-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-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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | int 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, int 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 | -------------------------------------------------------------------------------- /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 | static void append_filter(QQmlListProperty* list, Filter* filter); 34 | static int count_filter(QQmlListProperty* list); 35 | static Filter* at_filter(QQmlListProperty* list, int index); 36 | static void clear_filters(QQmlListProperty* list); 37 | }; 38 | 39 | class FilterContainerAttached : public QObject 40 | { 41 | Q_OBJECT 42 | Q_PROPERTY(QObject* container READ container WRITE setContainer NOTIFY containerChanged) 43 | 44 | public: 45 | FilterContainerAttached(QObject* object); 46 | ~FilterContainerAttached(); 47 | 48 | QObject* container() const; 49 | void setContainer(QObject* object); 50 | 51 | static FilterContainerAttached* qmlAttachedProperties(QObject* object); 52 | 53 | Q_SIGNALS: 54 | void containerChanged(); 55 | 56 | private: 57 | QPointer m_container = nullptr; 58 | Filter* m_filter = nullptr; 59 | }; 60 | 61 | } 62 | 63 | #define FilterContainer_iid "fr.grecko.SortFilterProxyModel.FilterContainer" 64 | Q_DECLARE_INTERFACE(qqsfpm::FilterContainer, FilterContainer_iid) 65 | 66 | QML_DECLARE_TYPEINFO(qqsfpm::FilterContainerAttached, QML_HAS_ATTACHED_PROPERTIES) 67 | 68 | #endif // FILTERCONTAINER_H 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /filters/rangefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "rangefilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype RangeFilter 7 | \inherits RoleFilter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Filters 10 | \brief Filters rows between boundary values. 11 | 12 | A RangeFilter is a \l RoleFilter that accepts rows if their data is between the filter's minimum and maximum value. 13 | 14 | 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 : 15 | \code 16 | RangeSlider { 17 | id: priceRangeSlider 18 | } 19 | 20 | SortFilterProxyModel { 21 | sourceModel: priceModel 22 | filters: RangeFilter { 23 | roleName: "price" 24 | minimumValue: priceRangeSlider.first.value 25 | maximumValue: priceRangeSlider.second.value 26 | } 27 | } 28 | \endcode 29 | */ 30 | 31 | /*! 32 | \qmlproperty int RangeFilter::minimumValue 33 | 34 | This property holds the minimumValue of the filter. 35 | Rows with a value lower than \c minimumValue will be rejected. 36 | 37 | By default, no value is set. 38 | 39 | \sa minimumInclusive 40 | */ 41 | QVariant RangeFilter::minimumValue() const 42 | { 43 | return m_minimumValue; 44 | } 45 | 46 | void RangeFilter::setMinimumValue(QVariant minimumValue) 47 | { 48 | if (m_minimumValue == minimumValue) 49 | return; 50 | 51 | m_minimumValue = minimumValue; 52 | Q_EMIT minimumValueChanged(); 53 | invalidate(); 54 | } 55 | 56 | /*! 57 | \qmlproperty int RangeFilter::minimumInclusive 58 | 59 | This property holds whether the \l minimumValue is inclusive. 60 | 61 | By default, the \l minimumValue is inclusive. 62 | 63 | \sa minimumValue 64 | */ 65 | bool RangeFilter::minimumInclusive() const 66 | { 67 | return m_minimumInclusive; 68 | } 69 | 70 | void RangeFilter::setMinimumInclusive(bool minimumInclusive) 71 | { 72 | if (m_minimumInclusive == minimumInclusive) 73 | return; 74 | 75 | m_minimumInclusive = minimumInclusive; 76 | Q_EMIT minimumInclusiveChanged(); 77 | invalidate(); 78 | } 79 | 80 | /*! 81 | \qmlproperty int RangeFilter::maximumValue 82 | 83 | This property holds the maximumValue of the filter. 84 | Rows with a value higher than \c maximumValue will be rejected. 85 | 86 | By default, no value is set. 87 | 88 | \sa maximumInclusive 89 | */ 90 | QVariant RangeFilter::maximumValue() const 91 | { 92 | return m_maximumValue; 93 | } 94 | 95 | void RangeFilter::setMaximumValue(QVariant maximumValue) 96 | { 97 | if (m_maximumValue == maximumValue) 98 | return; 99 | 100 | m_maximumValue = maximumValue; 101 | Q_EMIT maximumValueChanged(); 102 | invalidate(); 103 | } 104 | 105 | /*! 106 | \qmlproperty int RangeFilter::maximumInclusive 107 | 108 | This property holds whether the \l minimumValue is inclusive. 109 | 110 | By default, the \l minimumValue is inclusive. 111 | 112 | \sa minimumValue 113 | */ 114 | bool RangeFilter::maximumInclusive() const 115 | { 116 | return m_maximumInclusive; 117 | } 118 | 119 | void RangeFilter::setMaximumInclusive(bool maximumInclusive) 120 | { 121 | if (m_maximumInclusive == maximumInclusive) 122 | return; 123 | 124 | m_maximumInclusive = maximumInclusive; 125 | Q_EMIT maximumInclusiveChanged(); 126 | invalidate(); 127 | } 128 | 129 | bool RangeFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 130 | { 131 | QVariant value = sourceData(sourceIndex, proxyModel); 132 | bool lessThanMin = m_minimumValue.isValid() && 133 | (m_minimumInclusive ? value < m_minimumValue : value <= m_minimumValue); 134 | bool moreThanMax = m_maximumValue.isValid() && 135 | (m_maximumInclusive ? value > m_maximumValue : value >= m_maximumValue); 136 | return !(lessThanMin || moreThanMax); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | QString RegExpFilter::pattern() const 39 | { 40 | return m_pattern; 41 | } 42 | 43 | void RegExpFilter::setPattern(const QString& pattern) 44 | { 45 | if (m_pattern == pattern) 46 | return; 47 | 48 | m_pattern = pattern; 49 | m_regExp.setPattern(pattern); 50 | Q_EMIT patternChanged(); 51 | invalidate(); 52 | } 53 | 54 | /*! 55 | \qmlproperty enum RegExpFilter::syntax 56 | 57 | The pattern used to filter the contents of the source model. 58 | 59 | Only the source model's value having their \l RoleFilter::roleName data matching this \l pattern with the specified \l syntax will be kept. 60 | 61 | \value RegExpFilter.RegExp A rich Perl-like pattern matching syntax. This is the default. 62 | \value RegExpFilter.Wildcard This provides a simple pattern matching syntax similar to that used by shells (command interpreters) for "file globbing". 63 | \value RegExpFilter.FixedString The pattern is a fixed string. This is equivalent to using the RegExp pattern on a string in which all metacharacters are escaped. 64 | \value RegExpFilter.RegExp2 Like RegExp, but with greedy quantifiers. 65 | \value RegExpFilter.WildcardUnix This is similar to Wildcard but with the behavior of a Unix shell. The wildcard characters can be escaped with the character "\". 66 | \value RegExpFilter.W3CXmlSchema11 The pattern is a regular expression as defined by the W3C XML Schema 1.1 specification. 67 | 68 | \sa pattern 69 | */ 70 | RegExpFilter::PatternSyntax RegExpFilter::syntax() const 71 | { 72 | return m_syntax; 73 | } 74 | 75 | void RegExpFilter::setSyntax(RegExpFilter::PatternSyntax syntax) 76 | { 77 | if (m_syntax == syntax) 78 | return; 79 | 80 | m_syntax = syntax; 81 | m_regExp.setPatternSyntax(static_cast(syntax)); 82 | Q_EMIT syntaxChanged(); 83 | invalidate(); 84 | } 85 | 86 | /*! 87 | \qmlproperty Qt::CaseSensitivity RegExpFilter::caseSensitivity 88 | 89 | This property holds the caseSensitivity of the filter. 90 | */ 91 | Qt::CaseSensitivity RegExpFilter::caseSensitivity() const 92 | { 93 | return m_caseSensitivity; 94 | } 95 | 96 | void RegExpFilter::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 97 | { 98 | if (m_caseSensitivity == caseSensitivity) 99 | return; 100 | 101 | m_caseSensitivity = caseSensitivity; 102 | m_regExp.setCaseSensitivity(caseSensitivity); 103 | Q_EMIT caseSensitivityChanged(); 104 | invalidate(); 105 | } 106 | 107 | bool RegExpFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 108 | { 109 | QString string = sourceData(sourceIndex, proxyModel).toString(); 110 | return m_regExp.indexIn(string) != -1; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /filters/regexpfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef REGEXPFILTER_H 2 | #define REGEXPFILTER_H 3 | 4 | #include "rolefilter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class RegExpFilter : public RoleFilter { 9 | Q_OBJECT 10 | Q_PROPERTY(QString pattern READ pattern WRITE setPattern NOTIFY patternChanged) 11 | Q_PROPERTY(PatternSyntax syntax READ syntax WRITE setSyntax NOTIFY syntaxChanged) 12 | Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) 13 | 14 | public: 15 | enum PatternSyntax { 16 | RegExp = QRegExp::RegExp, 17 | Wildcard = QRegExp::Wildcard, 18 | FixedString = QRegExp::FixedString, 19 | RegExp2 = QRegExp::RegExp2, 20 | WildcardUnix = QRegExp::WildcardUnix, 21 | W3CXmlSchema11 = QRegExp::W3CXmlSchema11 }; 22 | Q_ENUMS(PatternSyntax) 23 | 24 | using RoleFilter::RoleFilter; 25 | 26 | QString pattern() const; 27 | void setPattern(const QString& pattern); 28 | 29 | PatternSyntax syntax() const; 30 | void setSyntax(PatternSyntax syntax); 31 | 32 | Qt::CaseSensitivity caseSensitivity() const; 33 | void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); 34 | 35 | protected: 36 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 37 | 38 | Q_SIGNALS: 39 | void patternChanged(); 40 | void syntaxChanged(); 41 | void caseSensitivityChanged(); 42 | 43 | private: 44 | QRegExp m_regExp; 45 | Qt::CaseSensitivity m_caseSensitivity = m_regExp.caseSensitivity(); 46 | PatternSyntax m_syntax = static_cast(m_regExp.patternSyntax()); 47 | QString m_pattern = m_regExp.pattern(); 48 | }; 49 | 50 | } 51 | 52 | #endif // REGEXPFILTER_H 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | return !m_value.isValid() || m_value == sourceData(sourceIndex, proxyModel); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | int 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, int 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 | -------------------------------------------------------------------------------- /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 | static void append_proxyRole(QQmlListProperty* list, ProxyRole* proxyRole); 32 | static int count_proxyRole(QQmlListProperty* list); 33 | static ProxyRole* at_proxyRole(QQmlListProperty* list, int index); 34 | static void clear_proxyRoles(QQmlListProperty* list); 35 | }; 36 | 37 | } 38 | 39 | #define ProxyRoleContainer_iid "fr.grecko.SortFilterProxyModel.ProxyRoleContainer" 40 | Q_DECLARE_INTERFACE(qqsfpm::ProxyRoleContainer, ProxyRoleContainer_iid) 41 | 42 | #endif // PROXYROLECONTAINER_H 43 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /sorters/rolesorter.cpp: -------------------------------------------------------------------------------- 1 | #include "rolesorter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype RoleSorter 8 | \inherits Sorter 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Sorters 11 | \brief Sorts rows based on a source model role. 12 | 13 | A RoleSorter is a simple \l Sorter that sorts rows based on a source model role. 14 | 15 | In the following example, rows with be sorted by their \c lastName role : 16 | \code 17 | SortFilterProxyModel { 18 | sourceModel: contactModel 19 | sorters: RoleSorter { roleName: "lastName" } 20 | } 21 | \endcode 22 | */ 23 | 24 | /*! 25 | \qmlproperty string RoleSorter::roleName 26 | 27 | This property holds the role name that the sorter is using to query the source model's data when sorting items. 28 | */ 29 | const QString& RoleSorter::roleName() const 30 | { 31 | return m_roleName; 32 | } 33 | 34 | void RoleSorter::setRoleName(const QString& roleName) 35 | { 36 | if (m_roleName == roleName) 37 | return; 38 | 39 | m_roleName = roleName; 40 | Q_EMIT roleNameChanged(); 41 | invalidate(); 42 | } 43 | 44 | QPair RoleSorter::sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 45 | { 46 | QPair pair; 47 | int role = proxyModel.roleForName(m_roleName); 48 | 49 | if (role == -1) 50 | return pair; 51 | 52 | pair.first = proxyModel.sourceData(sourceLeft, role); 53 | pair.second = proxyModel.sourceData(sourceRight, role); 54 | return pair; 55 | } 56 | 57 | int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 58 | { 59 | QPair pair = sourceData(sourceLeft, sourceRight, proxyModel); 60 | QVariant leftValue = pair.first; 61 | QVariant rightValue = pair.second; 62 | if (leftValue < rightValue) 63 | return -1; 64 | if (leftValue > rightValue) 65 | return 1; 66 | return 0; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | int 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, int 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 | -------------------------------------------------------------------------------- /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 | static void append_sorter(QQmlListProperty* list, Sorter* sorter); 34 | static int count_sorter(QQmlListProperty* list); 35 | static Sorter* at_sorter(QQmlListProperty* list, int index); 36 | static void clear_sorters(QQmlListProperty* list); 37 | }; 38 | 39 | class SorterContainerAttached : public QObject 40 | { 41 | Q_OBJECT 42 | Q_PROPERTY(QObject* container READ container WRITE setContainer NOTIFY containerChanged) 43 | 44 | public: 45 | SorterContainerAttached(QObject* object); 46 | ~SorterContainerAttached(); 47 | 48 | QObject* container() const; 49 | void setContainer(QObject* object); 50 | 51 | static SorterContainerAttached* qmlAttachedProperties(QObject* object); 52 | 53 | Q_SIGNALS: 54 | void containerChanged(); 55 | 56 | private: 57 | QPointer m_container = nullptr; 58 | Sorter* m_sorter = nullptr; 59 | }; 60 | 61 | } 62 | 63 | #define SorterContainer_iid "fr.grecko.SortFilterProxyModel.SorterContainer" 64 | Q_DECLARE_INTERFACE(qqsfpm::SorterContainer, SorterContainer_iid) 65 | 66 | QML_DECLARE_TYPEINFO(qqsfpm::SorterContainerAttached, QML_HAS_ATTACHED_PROPERTIES) 67 | 68 | #endif // SORTERSSORTERCONTAINER_H 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | for (var i = 0; i < testModel.count; i++) 64 | { 65 | var modelValue = testModel.get(i, sorter.roleName); 66 | verify(modelValue === sorter.expectedValues[i], 67 | "Expected testModel value " + sorter.expectedValues[i] + ", actual: " + modelValue); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/tst_sortfilterproxymodel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | QUICK_TEST_MAIN(SortFilterProxyModel) 3 | -------------------------------------------------------------------------------- /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_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 | for (var i = 0; i < testModel.count; i++) 79 | { 80 | var modelValue = testModel.get(i, sorter.roleName); 81 | verify(modelValue === sorter.expectedValues[i], 82 | "Expected testModel value " + sorter.expectedValues[i] + ", actual: " + modelValue); 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------