mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-06-06 04:41:58 +02:00
[ui] improve SortFilterDelegateModel
* use variant for filterValue and implement different logics to test filter matching based on value type * allow redefinition of modelData and respectFilter functions to customize logic from outisde if need be * add convenient 'reverseSortOrder' method
This commit is contained in:
parent
53764812bd
commit
d2da971169
2 changed files with 52 additions and 16 deletions
|
@ -5,21 +5,27 @@ import QtQuick.Controls 2.3
|
||||||
/**
|
/**
|
||||||
* SortFilderDelegateModel adds sorting and filtering capabilities on a source model.
|
* SortFilderDelegateModel adds sorting and filtering capabilities on a source model.
|
||||||
*
|
*
|
||||||
* Filter only works on string properties for now.
|
* The way model data is accessed can be overriden by redefining the modelData function.
|
||||||
|
* This is useful if the value is not directly accessible from the model and needs
|
||||||
|
* some extra logic.
|
||||||
|
*
|
||||||
|
* Regarding filtering, any type of value can be used as 'filterValue' (variant).
|
||||||
|
* Filtering behavior can also be overriden by redefining the respectFilter function.
|
||||||
|
*
|
||||||
* Based on http://doc.qt.io/qt-5/qtquick-tutorials-dynamicview-dynamicview4-example.html
|
* Based on http://doc.qt.io/qt-5/qtquick-tutorials-dynamicview-dynamicview4-example.html
|
||||||
*/
|
*/
|
||||||
DelegateModel {
|
DelegateModel {
|
||||||
id: sortFilterModel
|
id: sortFilterModel
|
||||||
|
|
||||||
property string sortRole: "name" /// the role to use for sorting
|
property string sortRole: "" /// the role to use for sorting
|
||||||
property int sortOrder: Qt.AscendingOrder /// the sorting order
|
property int sortOrder: Qt.AscendingOrder /// the sorting order
|
||||||
property string filterRole: "" /// the role to use for filtering
|
property string filterRole: "" /// the role to use for filtering
|
||||||
property string textFilter: "" /// the text used as filter
|
property variant filterValue /// the value to use as filter
|
||||||
|
|
||||||
onSortRoleChanged: invalidateSort()
|
onSortRoleChanged: invalidateSort()
|
||||||
onSortOrderChanged: invalidateSort()
|
onSortOrderChanged: invalidateSort()
|
||||||
onFilterRoleChanged: invalidateFilter()
|
onFilterRoleChanged: invalidateFilter()
|
||||||
onTextFilterChanged: invalidateFilter()
|
onFilterValueChanged: invalidateFilter()
|
||||||
|
|
||||||
// display "filtered" group
|
// display "filtered" group
|
||||||
filterOnGroup: "filtered"
|
filterOnGroup: "filtered"
|
||||||
|
@ -36,7 +42,14 @@ DelegateModel {
|
||||||
includeByDefault: true
|
includeByDefault: true
|
||||||
// if the source model changes, perform sorting and filtering
|
// if the source model changes, perform sorting and filtering
|
||||||
onChanged: {
|
onChanged: {
|
||||||
sort()
|
// no sorting: move everything from unsorted to sorted group
|
||||||
|
if(sortRole == "") {
|
||||||
|
unsortedItems.setGroups(0, unsortedItems.count, ["items"])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sort()
|
||||||
|
}
|
||||||
|
// perform filter invalidation in both cases
|
||||||
invalidateFilter()
|
invalidateFilter()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -47,12 +60,41 @@ DelegateModel {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// Get data from model for 'roleName'
|
||||||
|
function modelData(item, roleName) {
|
||||||
|
return item.model[roleName]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether 'value' respects 'filter' condition
|
||||||
|
*
|
||||||
|
* The test is based on the value's type:
|
||||||
|
* - String: check if 'value' contains 'filter' (case insensitive)
|
||||||
|
* - any other type: test for equality (===)
|
||||||
|
*
|
||||||
|
* TODO: add case sensitivity / whole word options for Strings
|
||||||
|
*/
|
||||||
|
function respectFilter(value, filter) {
|
||||||
|
switch(value.constructor.name)
|
||||||
|
{
|
||||||
|
case "String":
|
||||||
|
return value.toLowerCase().search(filter.toLowerCase()) >= 0
|
||||||
|
default:
|
||||||
|
return value === filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reverse sort order (toggle between Qt.AscendingOrder / Qt.DescendingOrder)
|
||||||
|
function reverseSortOrder() {
|
||||||
|
sortOrder = sortOrder == Qt.AscendingOrder ? Qt.DescendingOrder : Qt.AscendingOrder
|
||||||
|
}
|
||||||
|
|
||||||
property var lessThan: [
|
property var lessThan: [
|
||||||
function(left, right) { return left[sortRole] < right[sortRole] }
|
function(left, right) { return modelData(left, sortRole) < modelData(right, sortRole) }
|
||||||
]
|
]
|
||||||
|
|
||||||
function invalidateSort() {
|
function invalidateSort() {
|
||||||
if(!sortFilterModel.model)
|
if(!sortFilterModel.model || !sortFilterModel.model.count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// move everything from "items" to "unsorted
|
// move everything from "items" to "unsorted
|
||||||
|
@ -60,12 +102,6 @@ DelegateModel {
|
||||||
items.setGroups(0, items.count, ["unsorted"])
|
items.setGroups(0, items.count, ["unsorted"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add option for case sensitivity / whole word
|
|
||||||
function containsText(reference, text)
|
|
||||||
{
|
|
||||||
return reference.toLowerCase().search(text.toLowerCase()) >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invalidate filtering
|
/// Invalidate filtering
|
||||||
function invalidateFilter() {
|
function invalidateFilter() {
|
||||||
// no filtering, add everything to the filtered group
|
// no filtering, add everything to the filtered group
|
||||||
|
@ -78,7 +114,7 @@ DelegateModel {
|
||||||
for(var i=0; i < items.count; ++i)
|
for(var i=0; i < items.count; ++i)
|
||||||
{
|
{
|
||||||
// if the property value contains filterText, add it to the filtered group
|
// if the property value contains filterText, add it to the filtered group
|
||||||
if(containsText(items.get(i).model[filterRole], textFilter))
|
if(respectFilter(modelData(items.get(i), filterRole), filterValue))
|
||||||
{
|
{
|
||||||
items.addGroups(items.get(i), 1, "filtered")
|
items.addGroups(items.get(i), 1, "filtered")
|
||||||
}
|
}
|
||||||
|
@ -96,7 +132,7 @@ DelegateModel {
|
||||||
var upper = items.count
|
var upper = items.count
|
||||||
while (lower < upper) {
|
while (lower < upper) {
|
||||||
var middle = Math.floor(lower + (upper - lower) / 2)
|
var middle = Math.floor(lower + (upper - lower) / 2)
|
||||||
var result = lessThan(item.model, items.get(middle).model)
|
var result = lessThan(item, items.get(middle))
|
||||||
if(sortOrder == Qt.DescendingOrder)
|
if(sortOrder == Qt.DescendingOrder)
|
||||||
result = !result
|
result = !result
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
|
@ -125,7 +125,7 @@ Pane {
|
||||||
model: metadataModel
|
model: metadataModel
|
||||||
sortRole: "raw"
|
sortRole: "raw"
|
||||||
filterRole: "raw"
|
filterRole: "raw"
|
||||||
textFilter: filter.text
|
filterValue: filter.text
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
Label {
|
Label {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue