feature: add StringSorter type
This commit is contained in:
parent
2f555860ff
commit
9e37e58eef
128
sorter.cpp
128
sorter.cpp
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
38
sorter.h
38
sorter.h
|
@ -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
|
||||
|
|
|
@ -19,4 +19,5 @@ OTHER_FILES += \
|
|||
tst_sorters.qml \
|
||||
tst_helpers.qml \
|
||||
tst_builtins.qml \
|
||||
tst_rolesorter.qml
|
||||
tst_rolesorter.qml \
|
||||
tst_stringsorter.qml
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue