diff --git a/sorter.cpp b/sorter.cpp index 9d8f917..b3a9be3 100644 --- a/sorter.cpp +++ b/sorter.cpp @@ -104,9 +104,116 @@ int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& source return 0; } +const QQmlScriptString& ExpressionSorter::expression() const +{ + return m_scriptString; +} + +void ExpressionSorter::setExpression(const QQmlScriptString& scriptString) +{ + if (m_scriptString == scriptString) + return; + + m_scriptString = scriptString; + updateExpression(); + + emit expressionChanged(); + emit sorterChanged(); +} + +bool evaluateBoolExpression(QQmlExpression& expression) +{ + QVariant variantResult = expression.evaluate(); + if (expression.hasError()) { + qWarning() << expression.error(); + return false; + } + if (variantResult.canConvert()) { + 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 +{ + if (!m_scriptString.isEmpty()) { + QVariantMap modelLeftMap, modelRightMap; + QHash 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::proxyModelCompleted() +{ + updateContext(); +} + +void ExpressionSorter::updateContext() +{ + if (!proxyModel()) + return; + + 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, &Sorter::sorterChanged); + m_expression->setNotifyOnValueChanged(true); + m_expression->evaluate(); +} + void registerSorterTypes() { qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Sorter", "Sorter is an abstract class"); qmlRegisterType("SortFilterProxyModel", 0, 2, "RoleSorter"); + qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionSorter"); } Q_COREAPP_STARTUP_FUNCTION(registerSorterTypes) diff --git a/sorter.h b/sorter.h index e01fda2..38be433 100644 --- a/sorter.h +++ b/sorter.h @@ -2,6 +2,7 @@ #define SORTER_H #include +#include #include "qqmlsortfilterproxymodel.h" namespace qqsfpm { @@ -69,6 +70,33 @@ private: QString m_roleName; }; +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); + +signals: + void expressionChanged(); + +protected: + int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const override; + void proxyModelCompleted() override; + +private: + void updateContext(); + void updateExpression(); + + QQmlScriptString m_scriptString; + QQmlExpression* m_expression = nullptr; + QQmlContext* m_context = nullptr; +}; + } #endif // SORTER_H