feature: add StringSorter type

This commit is contained in:
Pierre-Yves Siret 2017-09-12 01:25:11 +02:00
parent 2f555860ff
commit 9e37e58eef
4 changed files with 253 additions and 3 deletions

View File

@ -154,10 +154,22 @@ void RoleSorter::setRoleName(const QString& roleName)
sorterChanged();
}
QPair<QVariant, QVariant> RoleSorter::sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight) const
{
QPair<QVariant, QVariant> pair;
if (QQmlSortFilterProxyModel* proxy = proxyModel()) {
int role = proxy->roleForName(m_roleName);
pair.first = proxy->sourceData(sourceLeft, role);
pair.second = proxy->sourceData(sourceRight, role);
}
return pair;
}
int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& sourceRight) const
{
QVariant leftValue = proxyModel()->sourceData(sourceLeft, m_roleName);
QVariant rightValue = proxyModel()->sourceData(sourceRight, m_roleName);
QPair<QVariant, QVariant> pair = sourceData(sourceLeft, sourceRight);
QVariant leftValue = pair.first;
QVariant rightValue = pair.second;
if (leftValue < rightValue)
return -1;
if (leftValue > rightValue)
@ -165,6 +177,117 @@ int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& source
return 0;
}
/*!
\qmltype StringSorter
\inherits RoleSorter
\inqmlmodule SortFilterProxyModel
\brief Sorts rows based on a source model string role
\l StringSorter is a specialized \l RoleSorter that sorts rows based on a source model string role.
\l StringSorter compares strings according to a localized collation algorithm.
In the following example, rows with be sorted by their \c lastName role :
\code
SortFilterProxyModel {
sourceModel: contactModel
sorters: StringSorter { roleName: "lastName" }
}
\endcode
*/
/*!
\qmlproperty Qt.CaseSensitivity StringSorter::caseSensitivity
This property holds the case sensitivity of the sorter.
*/
Qt::CaseSensitivity StringSorter::caseSensitivity() const
{
return m_collator.caseSensitivity();
}
void StringSorter::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
{
if (m_collator.caseSensitivity() == caseSensitivity)
return;
m_collator.setCaseSensitivity(caseSensitivity);
Q_EMIT caseSensitivityChanged();
sorterChanged();
}
/*!
\qmlproperty bool StringSorter::ignorePunctation
This property holds whether the sorter ignores punctation.
if \c ignorePunctuation is \c true, punctuation characters and symbols are ignored when determining sort order.
\note This property is not currently supported on Apple platforms or if Qt is configured to not use ICU on Linux.
*/
bool StringSorter::ignorePunctation() const
{
return m_collator.ignorePunctuation();
}
void StringSorter::setIgnorePunctation(bool ignorePunctation)
{
if (m_collator.ignorePunctuation() == ignorePunctation)
return;
m_collator.setIgnorePunctuation(ignorePunctation);
Q_EMIT ignorePunctationChanged();
sorterChanged();
}
/*!
\qmlproperty Locale StringSorter::locale
This property holds the locale of the sorter.
*/
QLocale StringSorter::locale() const
{
return m_collator.locale();
}
void StringSorter::setLocale(const QLocale &locale)
{
if (m_collator.locale() == locale)
return;
m_collator.setLocale(locale);
Q_EMIT localeChanged();
sorterChanged();
}
/*!
\qmlproperty bool StringSorter::numericMode
This property holds whether the numeric mode of the sorter is enabled.
This will enable proper sorting of numeric digits, so that e.g. 100 sorts after 99.
By default this mode is off.
*/
bool StringSorter::numericMode() const
{
return m_collator.numericMode();
}
void StringSorter::setNumericMode(bool numericMode)
{
if (m_collator.numericMode() == numericMode)
return;
m_collator.setNumericMode(numericMode);
Q_EMIT numericModeChanged();
sorterChanged();
}
int StringSorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
{
QPair<QVariant, QVariant> pair = sourceData(sourceLeft, sourceRight);
QString leftValue = pair.first.toString();
QString rightValue = pair.second.toString();
return m_collator.compare(leftValue, rightValue);
}
/*!
\qmltype ExpressionSorter
\inherits Sorter
@ -298,6 +421,7 @@ void ExpressionSorter::updateExpression()
void registerSorterTypes() {
qmlRegisterUncreatableType<Sorter>("SortFilterProxyModel", 0, 2, "Sorter", "Sorter is an abstract class");
qmlRegisterType<RoleSorter>("SortFilterProxyModel", 0, 2, "RoleSorter");
qmlRegisterType<StringSorter>("SortFilterProxyModel", 0, 2, "StringSorter");
qmlRegisterType<ExpressionSorter>("SortFilterProxyModel", 0, 2, "ExpressionSorter");
}

View File

@ -4,6 +4,7 @@
#include <QObject>
#include <QQmlExpression>
#include "qqmlsortfilterproxymodel.h"
#include "QCollator"
namespace qqsfpm {
@ -65,12 +66,49 @@ Q_SIGNALS:
void roleNameChanged();
protected:
QPair<QVariant, QVariant> sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight) const;
int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const override;
private:
QString m_roleName;
};
class StringSorter : public RoleSorter
{
Q_OBJECT
Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged)
Q_PROPERTY(bool ignorePunctation READ ignorePunctation WRITE setIgnorePunctation NOTIFY ignorePunctationChanged)
Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged)
Q_PROPERTY(bool numericMode READ numericMode WRITE setNumericMode NOTIFY numericModeChanged)
public:
using RoleSorter::RoleSorter;
Qt::CaseSensitivity caseSensitivity() const;
void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity);
bool ignorePunctation() const;
void setIgnorePunctation(bool ignorePunctation);
QLocale locale() const;
void setLocale(const QLocale& locale);
bool numericMode() const;
void setNumericMode(bool numericMode);
Q_SIGNALS:
void caseSensitivityChanged();
void ignorePunctationChanged();
void localeChanged();
void numericModeChanged();
protected:
int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const override;
private:
QCollator m_collator;
};
class ExpressionSorter : public Sorter
{
Q_OBJECT

View File

@ -19,4 +19,5 @@ OTHER_FILES += \
tst_sorters.qml \
tst_helpers.qml \
tst_builtins.qml \
tst_rolesorter.qml
tst_rolesorter.qml \
tst_stringsorter.qml

View File

@ -0,0 +1,87 @@
import QtQuick 2.0
import SortFilterProxyModel 0.2
import QtQml.Models 2.2
import QtTest 1.1
Item {
property list<StringSorter> sorters: [
StringSorter {
property string tag: "normal"
property var expectedValues: ["haha", "hähä", "hehe", "héhé", "hihi", "huhu"]
roleName: "accentRole"
},
StringSorter {
property string tag: "numericMode"
property var expectedValues: ["a1", "a20", "a30", "a99", "a100", "a1000"]
roleName: "numericRole"
numericMode: true
},
StringSorter {
property string tag: "nonNumericMode"
property var expectedValues: ["a1", "a100", "a1000", "a20", "a30", "a99"]
roleName: "numericRole"
numericMode: false
},
StringSorter {
//fails because of QTBUG-57034 and QTBUG-58621
property string tag: "caseSensitive"
property var expectedValues: ["A", "Z", "a", "b", "c", "z"]
roleName: "caseRole"
caseSensitivity: Qt.CaseSensitive
},
StringSorter {
property string tag: "nonCaseSensitive"
property var expectedValues: ["A", "a", "b", "c", "Z", "z"]
roleName: "caseRole"
caseSensitivity: Qt.CaseInsensitive
},
StringSorter {
property string tag: "ignorePunctuation"
property var expectedValues: ["a-a", "aa", "b-b", "b-c", "b.c", "bc"]
roleName: "punctuationRole"
ignorePunctation: true
},
StringSorter {
property string tag: "doNotIgnorePunctuation"
property var expectedValues: ["aa", "a-a", "b.c", "b-b", "bc", "b-c"]
roleName: "punctuationRole"
ignorePunctation: false
}
]
ListModel {
id: dataModel
ListElement { accentRole: "héhé"; numericRole: "a20"; caseRole: "b"; punctuationRole: "a-a"}
ListElement { accentRole: "hehe"; numericRole: "a1"; caseRole: "A"; punctuationRole: "aa"}
ListElement { accentRole: "haha"; numericRole: "a100"; caseRole: "a"; punctuationRole: "b-c"}
ListElement { accentRole: "huhu"; numericRole: "a99"; caseRole: "c"; punctuationRole: "b.c"}
ListElement { accentRole: "hihi"; numericRole: "a30"; caseRole: "Z"; punctuationRole: "bc"}
ListElement { accentRole: "hähä"; numericRole: "a1000"; caseRole: "z"; punctuationRole: "b-b"}
}
SortFilterProxyModel {
id: testModel
sourceModel: dataModel
}
TestCase {
name: "StringSorterTests"
function test_stringSorters_data() {
return sorters;
}
function test_stringSorters(sorter) {
testModel.sorters = sorter;
verify(testModel.count === sorter.expectedValues.length,
"Expected count " + sorter.expectedValues.length + ", actual count: " + testModel.count);
for (var i = 0; i < testModel.count; i++)
{
var modelValue = testModel.get(i, sorter.roleName);
verify(modelValue === sorter.expectedValues[i],
"Expected testModel value " + sorter.expectedValues[i] + ", actual: " + modelValue);
}
}
}
}