refactor: move sorters in separate files
This commit is contained in:
parent
df41e94e45
commit
2ee1936545
|
@ -3,7 +3,6 @@
|
|||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += $$PWD/qqmlsortfilterproxymodel.h \
|
||||
$$PWD/sorter.h \
|
||||
$$PWD/proxyrole.h \
|
||||
$$PWD/filters/filter.h \
|
||||
$$PWD/filters/filtercontainer.h \
|
||||
|
@ -15,10 +14,14 @@ HEADERS += $$PWD/qqmlsortfilterproxymodel.h \
|
|||
$$PWD/filters/expressionfilter.h \
|
||||
$$PWD/filters/filtercontainerfilter.h \
|
||||
$$PWD/filters/anyoffilter.h \
|
||||
$$PWD/filters/alloffilter.h
|
||||
$$PWD/filters/alloffilter.h \
|
||||
$$PWD/sorters/sorter.h \
|
||||
$$PWD/sorters/sortercontainer.h \
|
||||
$$PWD/sorters/rolesorter.h \
|
||||
$$PWD/sorters/stringsorter.h \
|
||||
$$PWD/sorters/expressionsorter.h
|
||||
|
||||
SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \
|
||||
$$PWD/sorter.cpp \
|
||||
$$PWD/proxyrole.cpp \
|
||||
$$PWD/filters/filter.cpp \
|
||||
$$PWD/filters/filtercontainer.cpp \
|
||||
|
@ -31,4 +34,10 @@ SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \
|
|||
$$PWD/filters/filtercontainerfilter.cpp \
|
||||
$$PWD/filters/anyoffilter.cpp \
|
||||
$$PWD/filters/alloffilter.cpp \
|
||||
$$PWD/filters/filtersqmltypes.cpp
|
||||
$$PWD/filters/filtersqmltypes.cpp \
|
||||
$$PWD/sorters/sorter.cpp \
|
||||
$$PWD/sorters/sortercontainer.cpp \
|
||||
$$PWD/sorters/rolesorter.cpp \
|
||||
$$PWD/sorters/stringsorter.cpp \
|
||||
$$PWD/sorters/expressionsorter.cpp \
|
||||
$$PWD/sorters/sortersqmltypes.cpp
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include <QtQml>
|
||||
#include <algorithm>
|
||||
#include "filters/filter.h"
|
||||
#include "sorter.h"
|
||||
#include "sorters/sorter.h"
|
||||
#include "proxyrole.h"
|
||||
|
||||
namespace qqsfpm {
|
||||
|
|
502
sorter.cpp
502
sorter.cpp
|
@ -1,502 +0,0 @@
|
|||
#include "sorter.h"
|
||||
#include "qqmlsortfilterproxymodel.h"
|
||||
#include <QtQml>
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
|
||||
SorterContainer::~SorterContainer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<Sorter*> SorterContainer::sorters() const
|
||||
{
|
||||
return m_sorters;
|
||||
}
|
||||
|
||||
void SorterContainer::appendSorter(Sorter* sorter)
|
||||
{
|
||||
m_sorters.append(sorter);
|
||||
onSorterAppended(sorter);
|
||||
}
|
||||
|
||||
void SorterContainer::removeSorter(Sorter *sorter)
|
||||
{
|
||||
m_sorters.removeOne(sorter);
|
||||
onSorterRemoved(sorter);
|
||||
}
|
||||
|
||||
void SorterContainer::clearSorters()
|
||||
{
|
||||
m_sorters.clear();
|
||||
onSortersCleared();
|
||||
}
|
||||
|
||||
QQmlListProperty<Sorter> SorterContainer::sortersListProperty()
|
||||
{
|
||||
return QQmlListProperty<Sorter>(reinterpret_cast<QObject*>(this), &m_sorters,
|
||||
&SorterContainer::append_sorter,
|
||||
&SorterContainer::count_sorter,
|
||||
&SorterContainer::at_sorter,
|
||||
&SorterContainer::clear_sorters);
|
||||
}
|
||||
|
||||
void SorterContainer::append_sorter(QQmlListProperty<Sorter>* list, Sorter* sorter)
|
||||
{
|
||||
if (!sorter)
|
||||
return;
|
||||
|
||||
SorterContainer* that = reinterpret_cast<SorterContainer*>(list->object);
|
||||
that->appendSorter(sorter);
|
||||
}
|
||||
|
||||
int SorterContainer::count_sorter(QQmlListProperty<Sorter>* list)
|
||||
{
|
||||
QList<Sorter*>* sorters = static_cast<QList<Sorter*>*>(list->data);
|
||||
return sorters->count();
|
||||
}
|
||||
|
||||
Sorter* SorterContainer::at_sorter(QQmlListProperty<Sorter>* list, int index)
|
||||
{
|
||||
QList<Sorter*>* sorters = static_cast<QList<Sorter*>*>(list->data);
|
||||
return sorters->at(index);
|
||||
}
|
||||
|
||||
void SorterContainer::clear_sorters(QQmlListProperty<Sorter> *list)
|
||||
{
|
||||
SorterContainer* that = reinterpret_cast<SorterContainer*>(list->object);
|
||||
that->clearSorters();
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmltype Sorter
|
||||
\inqmlmodule SortFilterProxyModel
|
||||
\brief Base type for the \l SortFilterProxyModel sorters
|
||||
|
||||
The Sorter 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 sorters types that inherit from it.
|
||||
Attempting to use the Sorter type directly will result in an error.
|
||||
*/
|
||||
|
||||
Sorter::Sorter(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
Sorter::~Sorter() = default;
|
||||
|
||||
/*!
|
||||
\qmlproperty bool Sorter::enabled
|
||||
|
||||
This property holds whether the sorter is enabled.
|
||||
A disabled sorter will not change the order of the rows.
|
||||
|
||||
By default, sorters are enabled.
|
||||
*/
|
||||
bool Sorter::enabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void Sorter::setEnabled(bool enabled)
|
||||
{
|
||||
if (m_enabled == enabled)
|
||||
return;
|
||||
|
||||
m_enabled = enabled;
|
||||
Q_EMIT enabledChanged();
|
||||
Q_EMIT invalidated();
|
||||
}
|
||||
|
||||
bool Sorter::ascendingOrder() const
|
||||
{
|
||||
return sortOrder() == Qt::AscendingOrder;
|
||||
}
|
||||
|
||||
void Sorter::setAscendingOrder(bool ascendingOrder)
|
||||
{
|
||||
setSortOrder(ascendingOrder ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\qmlproperty Qt::SortOrder Sorter::sortOrder
|
||||
|
||||
This property holds the sort order of this sorter.
|
||||
|
||||
\value Qt.AscendingOrder The items are sorted ascending e.g. starts with 'AAA' ends with 'ZZZ' in Latin-1 locales
|
||||
\value Qt.DescendingOrder The items are sorted descending e.g. starts with 'ZZZ' ends with 'AAA' in Latin-1 locales
|
||||
|
||||
By default, sorting is in ascending order.
|
||||
*/
|
||||
Qt::SortOrder Sorter::sortOrder() const
|
||||
{
|
||||
return m_sortOrder;
|
||||
}
|
||||
|
||||
void Sorter::setSortOrder(Qt::SortOrder sortOrder)
|
||||
{
|
||||
if (m_sortOrder == sortOrder)
|
||||
return;
|
||||
|
||||
m_sortOrder = sortOrder;
|
||||
Q_EMIT sortOrderChanged();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
int Sorter::compareRows(const QModelIndex &source_left, const QModelIndex &source_right, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
int comparison = compare(source_left, source_right, proxyModel);
|
||||
return (m_sortOrder == Qt::AscendingOrder) ? comparison : -comparison;
|
||||
}
|
||||
|
||||
int Sorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
if (lessThan(sourceLeft, sourceRight, proxyModel))
|
||||
return -1;
|
||||
if (lessThan(sourceRight, sourceLeft, proxyModel))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel)
|
||||
{
|
||||
Q_UNUSED(proxyModel)
|
||||
}
|
||||
|
||||
bool Sorter::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
Q_UNUSED(sourceLeft)
|
||||
Q_UNUSED(sourceRight)
|
||||
Q_UNUSED(proxyModel)
|
||||
return false;
|
||||
}
|
||||
|
||||
void Sorter::invalidate()
|
||||
{
|
||||
if (m_enabled)
|
||||
Q_EMIT invalidated();
|
||||
}
|
||||
|
||||
const QString& RoleSorter::roleName() const
|
||||
{
|
||||
return m_roleName;
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmltype RoleSorter
|
||||
\inherits Sorter
|
||||
\inqmlmodule SortFilterProxyModel
|
||||
\brief Sorts rows based on a source model role
|
||||
|
||||
A RoleSorter is a simple \l Sorter that sorts rows based on a source model role.
|
||||
|
||||
In the following example, rows with be sorted by their \c lastName role :
|
||||
\code
|
||||
SortFilterProxyModel {
|
||||
sourceModel: contactModel
|
||||
sorters: RoleSorter { roleName: "lastName" }
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlproperty string RoleSorter::roleName
|
||||
|
||||
This property holds the role name that the sorter is using to query the source model's data when sorting items.
|
||||
*/
|
||||
void RoleSorter::setRoleName(const QString& roleName)
|
||||
{
|
||||
if (m_roleName == roleName)
|
||||
return;
|
||||
|
||||
m_roleName = roleName;
|
||||
Q_EMIT roleNameChanged();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
QPair<QVariant, QVariant> RoleSorter::sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
QPair<QVariant, QVariant> pair;
|
||||
int role = proxyModel.roleForName(m_roleName);
|
||||
|
||||
if (role == -1)
|
||||
return pair;
|
||||
|
||||
pair.first = proxyModel.sourceData(sourceLeft, role);
|
||||
pair.second = proxyModel.sourceData(sourceRight, role);
|
||||
return pair;
|
||||
}
|
||||
|
||||
int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
QPair<QVariant, QVariant> pair = sourceData(sourceLeft, sourceRight, proxyModel);
|
||||
QVariant leftValue = pair.first;
|
||||
QVariant rightValue = pair.second;
|
||||
if (leftValue < rightValue)
|
||||
return -1;
|
||||
if (leftValue > rightValue)
|
||||
return 1;
|
||||
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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
int StringSorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
QPair<QVariant, QVariant> pair = sourceData(sourceLeft, sourceRight, proxyModel);
|
||||
QString leftValue = pair.first.toString();
|
||||
QString rightValue = pair.second.toString();
|
||||
return m_collator.compare(leftValue, rightValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmltype ExpressionSorter
|
||||
\inherits Sorter
|
||||
\inqmlmodule SortFilterProxyModel
|
||||
\brief Sorts row with a custom sorting
|
||||
|
||||
An ExpressionSorter is a \l Sorter allowing to implement custom sorting based on a javascript expression.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlproperty expression ExpressionSorter::expression
|
||||
|
||||
An expression to implement custom sorting. It must evaluate to a bool.
|
||||
It has the same syntax has a \l {http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html} {Property Binding}, except that it will be evaluated for each of the source model's rows.
|
||||
Model data is accessible for both rows with the \c modelLeft, and \c modelRight properties:
|
||||
|
||||
\code
|
||||
sorters: ExpressionSorter {
|
||||
expression: {
|
||||
return modelLeft.someRole < modelRight.someRole;
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
The \c index of the row is also available through \c modelLeft and \c modelRight.
|
||||
|
||||
The expression should return \c true if the value of the left item is less than the value of the right item, otherwise returns false.
|
||||
|
||||
This expression is reevaluated for a row every time its model data changes.
|
||||
When an external property (not \c index* or in \c 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.
|
||||
|
||||
A workaround to this problem is to access all the properties the expressions depends unconditionally at the beggining of the expression.
|
||||
*/
|
||||
const QQmlScriptString& ExpressionSorter::expression() const
|
||||
{
|
||||
return m_scriptString;
|
||||
}
|
||||
|
||||
void ExpressionSorter::setExpression(const QQmlScriptString& scriptString)
|
||||
{
|
||||
if (m_scriptString == scriptString)
|
||||
return;
|
||||
|
||||
m_scriptString = scriptString;
|
||||
updateExpression();
|
||||
|
||||
Q_EMIT expressionChanged();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ExpressionSorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel)
|
||||
{
|
||||
updateContext(proxyModel);
|
||||
}
|
||||
|
||||
bool evaluateBoolExpression(QQmlExpression& expression)
|
||||
{
|
||||
QVariant variantResult = expression.evaluate();
|
||||
if (expression.hasError()) {
|
||||
qWarning() << expression.error();
|
||||
return false;
|
||||
}
|
||||
if (variantResult.canConvert<bool>()) {
|
||||
return variantResult.toBool();
|
||||
} else {
|
||||
qWarning("%s:%i:%i : Can't convert result to bool",
|
||||
expression.sourceFile().toUtf8().data(),
|
||||
expression.lineNumber(),
|
||||
expression.columnNumber());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int ExpressionSorter::compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
if (!m_scriptString.isEmpty()) {
|
||||
QVariantMap modelLeftMap, modelRightMap;
|
||||
QHash<int, QByteArray> roles = proxyModel.roleNames();
|
||||
|
||||
QQmlContext context(qmlContext(this));
|
||||
|
||||
for (auto it = roles.cbegin(); it != roles.cend(); ++it) {
|
||||
modelLeftMap.insert(it.value(), proxyModel.sourceData(sourceLeft, it.key()));
|
||||
modelRightMap.insert(it.value(), proxyModel.sourceData(sourceRight, it.key()));
|
||||
}
|
||||
modelLeftMap.insert("index", sourceLeft.row());
|
||||
modelRightMap.insert("index", sourceRight.row());
|
||||
|
||||
QQmlExpression expression(m_scriptString, &context);
|
||||
|
||||
context.setContextProperty("modelLeft", modelLeftMap);
|
||||
context.setContextProperty("modelRight", modelRightMap);
|
||||
if (evaluateBoolExpression(expression))
|
||||
return -1;
|
||||
|
||||
context.setContextProperty("modelLeft", modelRightMap);
|
||||
context.setContextProperty("modelRight", modelLeftMap);
|
||||
if (evaluateBoolExpression(expression))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExpressionSorter::updateContext(const QQmlSortFilterProxyModel& proxyModel)
|
||||
{
|
||||
delete m_context;
|
||||
m_context = new QQmlContext(qmlContext(this), this);
|
||||
|
||||
QVariantMap modelLeftMap, modelRightMap;
|
||||
// what about roles changes ?
|
||||
|
||||
for (const QByteArray& roleName : proxyModel.roleNames().values()) {
|
||||
modelLeftMap.insert(roleName, QVariant());
|
||||
modelRightMap.insert(roleName, QVariant());
|
||||
}
|
||||
modelLeftMap.insert("index", -1);
|
||||
modelRightMap.insert("index", -1);
|
||||
|
||||
m_context->setContextProperty("modelLeft", modelLeftMap);
|
||||
m_context->setContextProperty("modelRight", modelRightMap);
|
||||
|
||||
updateExpression();
|
||||
}
|
||||
|
||||
void ExpressionSorter::updateExpression()
|
||||
{
|
||||
if (!m_context)
|
||||
return;
|
||||
|
||||
delete m_expression;
|
||||
m_expression = new QQmlExpression(m_scriptString, m_context, 0, this);
|
||||
connect(m_expression, &QQmlExpression::valueChanged, this, &ExpressionSorter::invalidate);
|
||||
m_expression->setNotifyOnValueChanged(true);
|
||||
m_expression->evaluate();
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
Q_COREAPP_STARTUP_FUNCTION(registerSorterTypes)
|
||||
|
||||
}
|
172
sorter.h
172
sorter.h
|
@ -1,172 +0,0 @@
|
|||
#ifndef SORTER_H
|
||||
#define SORTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlListProperty>
|
||||
#include <QQmlExpression>
|
||||
#include <QCollator>
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
class Sorter;
|
||||
class QQmlSortFilterProxyModel;
|
||||
|
||||
class SorterContainer {
|
||||
public:
|
||||
virtual ~SorterContainer();
|
||||
|
||||
QList<Sorter*> sorters() const;
|
||||
void appendSorter(Sorter* sorter);
|
||||
void removeSorter(Sorter* sorter);
|
||||
void clearSorters();
|
||||
|
||||
QQmlListProperty<Sorter> sortersListProperty();
|
||||
|
||||
protected:
|
||||
QList<Sorter*> m_sorters;
|
||||
|
||||
private:
|
||||
virtual void onSorterAppended(Sorter* sorter) = 0;
|
||||
virtual void onSorterRemoved(Sorter* sorter) = 0;
|
||||
virtual void onSortersCleared() = 0;
|
||||
|
||||
static void append_sorter(QQmlListProperty<Sorter>* list, Sorter* sorter);
|
||||
static int count_sorter(QQmlListProperty<Sorter>* list);
|
||||
static Sorter* at_sorter(QQmlListProperty<Sorter>* list, int index);
|
||||
static void clear_sorters(QQmlListProperty<Sorter>* list);
|
||||
};
|
||||
}
|
||||
#define SorterContainer_iid "fr.grecko.SortFilterProxyModel.SorterContainer"
|
||||
Q_DECLARE_INTERFACE(qqsfpm::SorterContainer, SorterContainer_iid)
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
class Sorter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
|
||||
Q_PROPERTY(bool ascendingOrder READ ascendingOrder WRITE setAscendingOrder NOTIFY sortOrderChanged)
|
||||
Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged)
|
||||
|
||||
public:
|
||||
Sorter(QObject* parent = nullptr);
|
||||
virtual ~Sorter() = 0;
|
||||
|
||||
bool enabled() const;
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
bool ascendingOrder() const;
|
||||
void setAscendingOrder(bool ascendingOrder);
|
||||
|
||||
Qt::SortOrder sortOrder() const;
|
||||
void setSortOrder(Qt::SortOrder sortOrder);
|
||||
|
||||
int compareRows(const QModelIndex& source_left, const QModelIndex& source_right, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
|
||||
virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel);
|
||||
|
||||
Q_SIGNALS:
|
||||
void enabledChanged();
|
||||
void sortOrderChanged();
|
||||
|
||||
void invalidated();
|
||||
|
||||
protected:
|
||||
virtual int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
virtual bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
void invalidate();
|
||||
|
||||
private:
|
||||
bool m_enabled = true;
|
||||
Qt::SortOrder m_sortOrder = Qt::AscendingOrder;
|
||||
};
|
||||
|
||||
class RoleSorter : public Sorter
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged)
|
||||
|
||||
public:
|
||||
using Sorter::Sorter;
|
||||
|
||||
const QString& roleName() const;
|
||||
void setRoleName(const QString& roleName);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roleNameChanged();
|
||||
|
||||
protected:
|
||||
QPair<QVariant, QVariant> sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) 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 QQmlSortFilterProxyModel& proxyModel) const override;
|
||||
|
||||
private:
|
||||
QCollator m_collator;
|
||||
};
|
||||
|
||||
class ExpressionSorter : public Sorter
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged)
|
||||
|
||||
public:
|
||||
using Sorter::Sorter;
|
||||
|
||||
const QQmlScriptString& expression() const;
|
||||
void setExpression(const QQmlScriptString& scriptString);
|
||||
|
||||
void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void expressionChanged();
|
||||
|
||||
protected:
|
||||
int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override;
|
||||
|
||||
private:
|
||||
void updateContext(const QQmlSortFilterProxyModel& proxyModel);
|
||||
void updateExpression();
|
||||
|
||||
QQmlScriptString m_scriptString;
|
||||
QQmlExpression* m_expression = nullptr;
|
||||
QQmlContext* m_context = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SORTER_H
|
|
@ -0,0 +1,145 @@
|
|||
#include "expressionsorter.h"
|
||||
#include "qqmlsortfilterproxymodel.h"
|
||||
#include <QtQml>
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
/*!
|
||||
\qmltype ExpressionSorter
|
||||
\inherits Sorter
|
||||
\inqmlmodule SortFilterProxyModel
|
||||
\brief Sorts row with a custom sorting
|
||||
|
||||
An ExpressionSorter is a \l Sorter allowing to implement custom sorting based on a javascript expression.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlproperty expression ExpressionSorter::expression
|
||||
|
||||
An expression to implement custom sorting. It must evaluate to a bool.
|
||||
It has the same syntax has a \l {http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html} {Property Binding}, except that it will be evaluated for each of the source model's rows.
|
||||
Model data is accessible for both rows with the \c modelLeft, and \c modelRight properties:
|
||||
|
||||
\code
|
||||
sorters: ExpressionSorter {
|
||||
expression: {
|
||||
return modelLeft.someRole < modelRight.someRole;
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
The \c index of the row is also available through \c modelLeft and \c modelRight.
|
||||
|
||||
The expression should return \c true if the value of the left item is less than the value of the right item, otherwise returns false.
|
||||
|
||||
This expression is reevaluated for a row every time its model data changes.
|
||||
When an external property (not \c index* or in \c 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.
|
||||
|
||||
A workaround to this problem is to access all the properties the expressions depends unconditionally at the beggining of the expression.
|
||||
*/
|
||||
const QQmlScriptString& ExpressionSorter::expression() const
|
||||
{
|
||||
return m_scriptString;
|
||||
}
|
||||
|
||||
void ExpressionSorter::setExpression(const QQmlScriptString& scriptString)
|
||||
{
|
||||
if (m_scriptString == scriptString)
|
||||
return;
|
||||
|
||||
m_scriptString = scriptString;
|
||||
updateExpression();
|
||||
|
||||
Q_EMIT expressionChanged();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ExpressionSorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel)
|
||||
{
|
||||
updateContext(proxyModel);
|
||||
}
|
||||
|
||||
bool evaluateBoolExpression(QQmlExpression& expression)
|
||||
{
|
||||
QVariant variantResult = expression.evaluate();
|
||||
if (expression.hasError()) {
|
||||
qWarning() << expression.error();
|
||||
return false;
|
||||
}
|
||||
if (variantResult.canConvert<bool>()) {
|
||||
return variantResult.toBool();
|
||||
} else {
|
||||
qWarning("%s:%i:%i : Can't convert result to bool",
|
||||
expression.sourceFile().toUtf8().data(),
|
||||
expression.lineNumber(),
|
||||
expression.columnNumber());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int ExpressionSorter::compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
if (!m_scriptString.isEmpty()) {
|
||||
QVariantMap modelLeftMap, modelRightMap;
|
||||
QHash<int, QByteArray> roles = proxyModel.roleNames();
|
||||
|
||||
QQmlContext context(qmlContext(this));
|
||||
|
||||
for (auto it = roles.cbegin(); it != roles.cend(); ++it) {
|
||||
modelLeftMap.insert(it.value(), proxyModel.sourceData(sourceLeft, it.key()));
|
||||
modelRightMap.insert(it.value(), proxyModel.sourceData(sourceRight, it.key()));
|
||||
}
|
||||
modelLeftMap.insert("index", sourceLeft.row());
|
||||
modelRightMap.insert("index", sourceRight.row());
|
||||
|
||||
QQmlExpression expression(m_scriptString, &context);
|
||||
|
||||
context.setContextProperty("modelLeft", modelLeftMap);
|
||||
context.setContextProperty("modelRight", modelRightMap);
|
||||
if (evaluateBoolExpression(expression))
|
||||
return -1;
|
||||
|
||||
context.setContextProperty("modelLeft", modelRightMap);
|
||||
context.setContextProperty("modelRight", modelLeftMap);
|
||||
if (evaluateBoolExpression(expression))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExpressionSorter::updateContext(const QQmlSortFilterProxyModel& proxyModel)
|
||||
{
|
||||
delete m_context;
|
||||
m_context = new QQmlContext(qmlContext(this), this);
|
||||
|
||||
QVariantMap modelLeftMap, modelRightMap;
|
||||
// what about roles changes ?
|
||||
|
||||
for (const QByteArray& roleName : proxyModel.roleNames().values()) {
|
||||
modelLeftMap.insert(roleName, QVariant());
|
||||
modelRightMap.insert(roleName, QVariant());
|
||||
}
|
||||
modelLeftMap.insert("index", -1);
|
||||
modelRightMap.insert("index", -1);
|
||||
|
||||
m_context->setContextProperty("modelLeft", modelLeftMap);
|
||||
m_context->setContextProperty("modelRight", modelRightMap);
|
||||
|
||||
updateExpression();
|
||||
}
|
||||
|
||||
void ExpressionSorter::updateExpression()
|
||||
{
|
||||
if (!m_context)
|
||||
return;
|
||||
|
||||
delete m_expression;
|
||||
m_expression = new QQmlExpression(m_scriptString, m_context, 0, this);
|
||||
connect(m_expression, &QQmlExpression::valueChanged, this, &ExpressionSorter::invalidate);
|
||||
m_expression->setNotifyOnValueChanged(true);
|
||||
m_expression->evaluate();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef EXPRESSIONSORTER_H
|
||||
#define EXPRESSIONSORTER_H
|
||||
|
||||
#include "sorter.h"
|
||||
#include <QQmlScriptString>
|
||||
|
||||
class QQmlExpression;
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
class QQmlSortFilterProxyModel;
|
||||
|
||||
class ExpressionSorter : public Sorter
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged)
|
||||
|
||||
public:
|
||||
using Sorter::Sorter;
|
||||
|
||||
const QQmlScriptString& expression() const;
|
||||
void setExpression(const QQmlScriptString& scriptString);
|
||||
|
||||
void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void expressionChanged();
|
||||
|
||||
protected:
|
||||
int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override;
|
||||
|
||||
private:
|
||||
void updateContext(const QQmlSortFilterProxyModel& proxyModel);
|
||||
void updateExpression();
|
||||
|
||||
QQmlScriptString m_scriptString;
|
||||
QQmlExpression* m_expression = nullptr;
|
||||
QQmlContext* m_context = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EXPRESSIONSORTER_H
|
|
@ -0,0 +1,68 @@
|
|||
#include "rolesorter.h"
|
||||
#include "qqmlsortfilterproxymodel.h"
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
/*!
|
||||
\qmltype RoleSorter
|
||||
\inherits Sorter
|
||||
\inqmlmodule SortFilterProxyModel
|
||||
\brief Sorts rows based on a source model role
|
||||
|
||||
A RoleSorter is a simple \l Sorter that sorts rows based on a source model role.
|
||||
|
||||
In the following example, rows with be sorted by their \c lastName role :
|
||||
\code
|
||||
SortFilterProxyModel {
|
||||
sourceModel: contactModel
|
||||
sorters: RoleSorter { roleName: "lastName" }
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlproperty string RoleSorter::roleName
|
||||
|
||||
This property holds the role name that the sorter is using to query the source model's data when sorting items.
|
||||
*/
|
||||
const QString& RoleSorter::roleName() const
|
||||
{
|
||||
return m_roleName;
|
||||
}
|
||||
|
||||
void RoleSorter::setRoleName(const QString& roleName)
|
||||
{
|
||||
if (m_roleName == roleName)
|
||||
return;
|
||||
|
||||
m_roleName = roleName;
|
||||
Q_EMIT roleNameChanged();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
QPair<QVariant, QVariant> RoleSorter::sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
QPair<QVariant, QVariant> pair;
|
||||
int role = proxyModel.roleForName(m_roleName);
|
||||
|
||||
if (role == -1)
|
||||
return pair;
|
||||
|
||||
pair.first = proxyModel.sourceData(sourceLeft, role);
|
||||
pair.second = proxyModel.sourceData(sourceRight, role);
|
||||
return pair;
|
||||
}
|
||||
|
||||
int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
QPair<QVariant, QVariant> pair = sourceData(sourceLeft, sourceRight, proxyModel);
|
||||
QVariant leftValue = pair.first;
|
||||
QVariant rightValue = pair.second;
|
||||
if (leftValue < rightValue)
|
||||
return -1;
|
||||
if (leftValue > rightValue)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef ROLESORTER_H
|
||||
#define ROLESORTER_H
|
||||
|
||||
#include "sorter.h"
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
class RoleSorter : public Sorter
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged)
|
||||
|
||||
public:
|
||||
using Sorter::Sorter;
|
||||
|
||||
const QString& roleName() const;
|
||||
void setRoleName(const QString& roleName);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roleNameChanged();
|
||||
|
||||
protected:
|
||||
QPair<QVariant, QVariant> sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override;
|
||||
|
||||
private:
|
||||
QString m_roleName;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ROLESORTER_H
|
|
@ -0,0 +1,116 @@
|
|||
#include "sorter.h"
|
||||
#include "qqmlsortfilterproxymodel.h"
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
/*!
|
||||
\qmltype Sorter
|
||||
\inqmlmodule SortFilterProxyModel
|
||||
\brief Base type for the \l SortFilterProxyModel sorters
|
||||
|
||||
The Sorter 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 sorters types that inherit from it.
|
||||
Attempting to use the Sorter type directly will result in an error.
|
||||
*/
|
||||
|
||||
Sorter::Sorter(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
Sorter::~Sorter() = default;
|
||||
|
||||
/*!
|
||||
\qmlproperty bool Sorter::enabled
|
||||
|
||||
This property holds whether the sorter is enabled.
|
||||
A disabled sorter will not change the order of the rows.
|
||||
|
||||
By default, sorters are enabled.
|
||||
*/
|
||||
bool Sorter::enabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void Sorter::setEnabled(bool enabled)
|
||||
{
|
||||
if (m_enabled == enabled)
|
||||
return;
|
||||
|
||||
m_enabled = enabled;
|
||||
Q_EMIT enabledChanged();
|
||||
Q_EMIT invalidated();
|
||||
}
|
||||
|
||||
bool Sorter::ascendingOrder() const
|
||||
{
|
||||
return sortOrder() == Qt::AscendingOrder;
|
||||
}
|
||||
|
||||
void Sorter::setAscendingOrder(bool ascendingOrder)
|
||||
{
|
||||
setSortOrder(ascendingOrder ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\qmlproperty Qt::SortOrder Sorter::sortOrder
|
||||
|
||||
This property holds the sort order of this sorter.
|
||||
|
||||
\value Qt.AscendingOrder The items are sorted ascending e.g. starts with 'AAA' ends with 'ZZZ' in Latin-1 locales
|
||||
\value Qt.DescendingOrder The items are sorted descending e.g. starts with 'ZZZ' ends with 'AAA' in Latin-1 locales
|
||||
|
||||
By default, sorting is in ascending order.
|
||||
*/
|
||||
Qt::SortOrder Sorter::sortOrder() const
|
||||
{
|
||||
return m_sortOrder;
|
||||
}
|
||||
|
||||
void Sorter::setSortOrder(Qt::SortOrder sortOrder)
|
||||
{
|
||||
if (m_sortOrder == sortOrder)
|
||||
return;
|
||||
|
||||
m_sortOrder = sortOrder;
|
||||
Q_EMIT sortOrderChanged();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
int Sorter::compareRows(const QModelIndex &source_left, const QModelIndex &source_right, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
int comparison = compare(source_left, source_right, proxyModel);
|
||||
return (m_sortOrder == Qt::AscendingOrder) ? comparison : -comparison;
|
||||
}
|
||||
|
||||
int Sorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
if (lessThan(sourceLeft, sourceRight, proxyModel))
|
||||
return -1;
|
||||
if (lessThan(sourceRight, sourceLeft, proxyModel))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel)
|
||||
{
|
||||
Q_UNUSED(proxyModel)
|
||||
}
|
||||
|
||||
bool Sorter::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
Q_UNUSED(sourceLeft)
|
||||
Q_UNUSED(sourceRight)
|
||||
Q_UNUSED(proxyModel)
|
||||
return false;
|
||||
}
|
||||
|
||||
void Sorter::invalidate()
|
||||
{
|
||||
if (m_enabled)
|
||||
Q_EMIT invalidated();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef SORTER_H
|
||||
#define SORTER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
class QQmlSortFilterProxyModel;
|
||||
|
||||
class Sorter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
|
||||
Q_PROPERTY(bool ascendingOrder READ ascendingOrder WRITE setAscendingOrder NOTIFY sortOrderChanged)
|
||||
Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged)
|
||||
|
||||
public:
|
||||
Sorter(QObject* parent = nullptr);
|
||||
virtual ~Sorter() = 0;
|
||||
|
||||
bool enabled() const;
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
bool ascendingOrder() const;
|
||||
void setAscendingOrder(bool ascendingOrder);
|
||||
|
||||
Qt::SortOrder sortOrder() const;
|
||||
void setSortOrder(Qt::SortOrder sortOrder);
|
||||
|
||||
int compareRows(const QModelIndex& source_left, const QModelIndex& source_right, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
|
||||
virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel);
|
||||
|
||||
Q_SIGNALS:
|
||||
void enabledChanged();
|
||||
void sortOrderChanged();
|
||||
|
||||
void invalidated();
|
||||
|
||||
protected:
|
||||
virtual int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
virtual bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const;
|
||||
void invalidate();
|
||||
|
||||
private:
|
||||
bool m_enabled = true;
|
||||
Qt::SortOrder m_sortOrder = Qt::AscendingOrder;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SORTER_H
|
|
@ -0,0 +1,69 @@
|
|||
#include "sortercontainer.h"
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
SorterContainer::~SorterContainer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<Sorter*> SorterContainer::sorters() const
|
||||
{
|
||||
return m_sorters;
|
||||
}
|
||||
|
||||
void SorterContainer::appendSorter(Sorter* sorter)
|
||||
{
|
||||
m_sorters.append(sorter);
|
||||
onSorterAppended(sorter);
|
||||
}
|
||||
|
||||
void SorterContainer::removeSorter(Sorter *sorter)
|
||||
{
|
||||
m_sorters.removeOne(sorter);
|
||||
onSorterRemoved(sorter);
|
||||
}
|
||||
|
||||
void SorterContainer::clearSorters()
|
||||
{
|
||||
m_sorters.clear();
|
||||
onSortersCleared();
|
||||
}
|
||||
|
||||
QQmlListProperty<Sorter> SorterContainer::sortersListProperty()
|
||||
{
|
||||
return QQmlListProperty<Sorter>(reinterpret_cast<QObject*>(this), &m_sorters,
|
||||
&SorterContainer::append_sorter,
|
||||
&SorterContainer::count_sorter,
|
||||
&SorterContainer::at_sorter,
|
||||
&SorterContainer::clear_sorters);
|
||||
}
|
||||
|
||||
void SorterContainer::append_sorter(QQmlListProperty<Sorter>* list, Sorter* sorter)
|
||||
{
|
||||
if (!sorter)
|
||||
return;
|
||||
|
||||
SorterContainer* that = reinterpret_cast<SorterContainer*>(list->object);
|
||||
that->appendSorter(sorter);
|
||||
}
|
||||
|
||||
int SorterContainer::count_sorter(QQmlListProperty<Sorter>* list)
|
||||
{
|
||||
QList<Sorter*>* sorters = static_cast<QList<Sorter*>*>(list->data);
|
||||
return sorters->count();
|
||||
}
|
||||
|
||||
Sorter* SorterContainer::at_sorter(QQmlListProperty<Sorter>* list, int index)
|
||||
{
|
||||
QList<Sorter*>* sorters = static_cast<QList<Sorter*>*>(list->data);
|
||||
return sorters->at(index);
|
||||
}
|
||||
|
||||
void SorterContainer::clear_sorters(QQmlListProperty<Sorter> *list)
|
||||
{
|
||||
SorterContainer* that = reinterpret_cast<SorterContainer*>(list->object);
|
||||
that->clearSorters();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef SORTERSSORTERCONTAINER_H
|
||||
#define SORTERSSORTERCONTAINER_H
|
||||
|
||||
#include <QList>
|
||||
#include <QQmlListProperty>
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
class Sorter;
|
||||
class QQmlSortFilterProxyModel;
|
||||
|
||||
class SorterContainer {
|
||||
public:
|
||||
virtual ~SorterContainer();
|
||||
|
||||
QList<Sorter*> sorters() const;
|
||||
void appendSorter(Sorter* sorter);
|
||||
void removeSorter(Sorter* sorter);
|
||||
void clearSorters();
|
||||
|
||||
QQmlListProperty<Sorter> sortersListProperty();
|
||||
|
||||
protected:
|
||||
QList<Sorter*> m_sorters;
|
||||
|
||||
private:
|
||||
virtual void onSorterAppended(Sorter* sorter) = 0;
|
||||
virtual void onSorterRemoved(Sorter* sorter) = 0;
|
||||
virtual void onSortersCleared() = 0;
|
||||
|
||||
static void append_sorter(QQmlListProperty<Sorter>* list, Sorter* sorter);
|
||||
static int count_sorter(QQmlListProperty<Sorter>* list);
|
||||
static Sorter* at_sorter(QQmlListProperty<Sorter>* list, int index);
|
||||
static void clear_sorters(QQmlListProperty<Sorter>* list);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define SorterContainer_iid "fr.grecko.SortFilterProxyModel.SorterContainer"
|
||||
Q_DECLARE_INTERFACE(qqsfpm::SorterContainer, SorterContainer_iid)
|
||||
|
||||
#endif // SORTERSSORTERCONTAINER_H
|
|
@ -0,0 +1,19 @@
|
|||
#include "sorter.h"
|
||||
#include "rolesorter.h"
|
||||
#include "stringsorter.h"
|
||||
#include "expressionsorter.h"
|
||||
#include <QQmlEngine>
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
Q_COREAPP_STARTUP_FUNCTION(registerSorterTypes)
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
#include "stringsorter.h"
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
/*!
|
||||
\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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
int StringSorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const
|
||||
{
|
||||
QPair<QVariant, QVariant> pair = sourceData(sourceLeft, sourceRight, proxyModel);
|
||||
QString leftValue = pair.first.toString();
|
||||
QString rightValue = pair.second.toString();
|
||||
return m_collator.compare(leftValue, rightValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef STRINGSORTER_H
|
||||
#define STRINGSORTER_H
|
||||
|
||||
#include "rolesorter.h"
|
||||
#include <QCollator>
|
||||
|
||||
namespace qqsfpm {
|
||||
|
||||
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 QQmlSortFilterProxyModel& proxyModel) const override;
|
||||
|
||||
private:
|
||||
QCollator m_collator;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // STRINGSORTER_H
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef INDEXSORTER_H
|
||||
#define INDEXSORTER_H
|
||||
|
||||
#include <sorter.h>
|
||||
#include "sorters/sorter.h"
|
||||
|
||||
class IndexSorter : public qqsfpm::Sorter
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue