feat: add ExpressionRole proxy role type

This commit is contained in:
Grecko 2017-09-22 13:44:10 +02:00
parent 4dc2eb1d7a
commit 38903de8f8
5 changed files with 165 additions and 1 deletions

View File

@ -218,10 +218,96 @@ void SwitchRole::clear_filters(QQmlListProperty<Filter> *list)
that->invalidate();
}
const QQmlScriptString& ExpressionRole::expression() const
{
return m_scriptString;
}
void ExpressionRole::setExpression(const QQmlScriptString& scriptString)
{
if (m_scriptString == scriptString)
return;
m_scriptString = scriptString;
updateExpression();
Q_EMIT expressionChanged();
invalidate();
}
void ExpressionRole::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel)
{
updateContext(proxyModel);
}
QVariant ExpressionRole::data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel)
{
if (!m_scriptString.isEmpty()) {
QVariantMap modelMap;
QHash<int, QByteArray> roles = proxyModel.roleNames();
QQmlContext context(qmlContext(this));
auto addToContext = [&] (const QString &name, const QVariant& value) {
context.setContextProperty(name, value);
modelMap.insert(name, value);
};
for (auto it = roles.cbegin(); it != roles.cend(); ++it)
addToContext(it.value(), proxyModel.sourceData(sourceIndex, it.key()));
addToContext("index", sourceIndex.row());
context.setContextProperty("model", modelMap);
QQmlExpression expression(m_scriptString, &context);
QVariant result = expression.evaluate();
if (expression.hasError()) {
qWarning() << expression.error();
return true;
}
return result;
}
return QVariant();
}
void ExpressionRole::updateContext(const QQmlSortFilterProxyModel& proxyModel)
{
delete m_context;
m_context = new QQmlContext(qmlContext(this), this);
// what about roles changes ?
QVariantMap modelMap;
auto addToContext = [&] (const QString &name, const QVariant& value) {
m_context->setContextProperty(name, value);
modelMap.insert(name, value);
};
for (const QByteArray& roleName : proxyModel.roleNames().values())
addToContext(roleName, QVariant());
addToContext("index", -1);
m_context->setContextProperty("model", modelMap);
updateExpression();
}
void ExpressionRole::updateExpression()
{
if (!m_context)
return;
delete m_expression;
m_expression = new QQmlExpression(m_scriptString, m_context, 0, this);
connect(m_expression, &QQmlExpression::valueChanged, this, &ExpressionRole::invalidate);
m_expression->setNotifyOnValueChanged(true);
m_expression->evaluate();
}
void registerProxyRoleTypes() {
qmlRegisterUncreatableType<ProxyRole>("SortFilterProxyModel", 0, 2, "ProxyRole", "ProxyRole is an abstract class");
qmlRegisterType<JoinRole>("SortFilterProxyModel", 0, 2, "JoinRole");
qmlRegisterType<SwitchRole>("SortFilterProxyModel", 0, 2, "SwitchRole");
qmlRegisterType<ExpressionRole>("SortFilterProxyModel", 0, 2, "ExpressionRole");
}
Q_COREAPP_STARTUP_FUNCTION(registerProxyRoleTypes)

View File

@ -3,6 +3,8 @@
#include <QObject>
#include <QMutex>
#include <QQmlScriptString>
#include <QQmlExpression>
#include <qqml.h>
#include "qqmlsortfilterproxymodel.h"
@ -118,6 +120,32 @@ private:
QList<Filter*> m_filters;
};
class ExpressionRole : public ProxyRole
{
Q_OBJECT
Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged)
public:
using ProxyRole::ProxyRole;
const QQmlScriptString& expression() const;
void setExpression(const QQmlScriptString& scriptString);
void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override;
Q_SIGNALS:
void expressionChanged();
private:
QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override;
void updateContext(const QQmlSortFilterProxyModel& proxyModel);
void updateExpression();
QQmlScriptString m_scriptString;
QQmlExpression* m_expression = nullptr;
QQmlContext* m_context = nullptr;
};
}
QML_DECLARE_TYPEINFO(qqsfpm::SwitchRole, QML_HAS_ATTACHED_PROPERTIES)

View File

@ -205,8 +205,14 @@ void QQmlSortFilterProxyModel::classBegin()
void QQmlSortFilterProxyModel::componentComplete()
{
m_completed = true;
for (const auto& filter : m_filters)
filter->proxyModelCompleted(*this);
for (const auto& sorter : m_sorters)
sorter->proxyModelCompleted(*this);
for (const auto& proxyRole : m_proxyRoles)
proxyRole->proxyModelCompleted(*this);
invalidate();
sort(0);
}

View File

@ -25,4 +25,5 @@ OTHER_FILES += \
tst_stringsorter.qml \
tst_proxyroles.qml \
tst_joinrole.qml \
tst_switchrole.qml
tst_switchrole.qml \
tst_expressionrole.qml

View File

@ -0,0 +1,43 @@
import QtQuick 2.0
import QtQml 2.2
import QtTest 1.1
import SortFilterProxyModel 0.2
import QtQml 2.2
Item {
property int c: 0
ListModel {
id: listModel
ListElement { a: 1; b: 2 }
}
SortFilterProxyModel {
id: testModel
sourceModel: listModel
proxyRoles: ExpressionRole {
name: "expressionRole"
expression: a + model.b + c
}
}
Instantiator {
id: instantiator
model: testModel
QtObject {
property string expressionRole: model.expressionRole
}
}
TestCase {
name: "ExpressionRole"
function test_expressionRole() {
fuzzyCompare(instantiator.object.expressionRole, 3, 1e-7);
listModel.setProperty(0, "b", 9);
fuzzyCompare(instantiator.object.expressionRole, 10, 1e-7);
c = 1327;
fuzzyCompare(instantiator.object.expressionRole, 1337, 1e-7);
}
}
}