Skip to content

Commit

Permalink
Added filter for Listview on mainpage.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdrescher2006 committed Jul 12, 2018
1 parent f3a25dd commit e2846a6
Show file tree
Hide file tree
Showing 61 changed files with 3,534 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Credits go to:
- Strava support: piggz

Workout icons are from here: https://de.icons8.com/ They are under this license: https://creativecommons.org/licenses/by-nd/3.0/
Filtered ListView is under MIT license: https://github.com/oKcerG/SortFilterProxyModel/blob/master/LICENSE
Filtered ListView is named "SortFilterProxyModel" and is under MIT license: https://github.com/oKcerG/SortFilterProxyModel/blob/master/LICENSE

License: GNU General Public License (GNU GPLv3)

Expand Down
1 change: 1 addition & 0 deletions harbour-laufhelden.pro
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,4 @@ DISTFILES += \
qml/units/second_de_male.wav \
qml/units/seconds_de_male.wav

include(libs/qmlsortfilterproxymodel/SortFilterProxyModel.pri)
52 changes: 52 additions & 0 deletions libs/qmlsortfilterproxymodel/SortFilterProxyModel.pri
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
!contains( CONFIG, c\+\+1[14] ): warning("SortFilterProxyModel needs at least c++11, add CONFIG += c++11 to your .pro")

INCLUDEPATH += $$PWD

HEADERS += $$PWD/qqmlsortfilterproxymodel.h \
$$PWD/filters/filter.h \
$$PWD/filters/filtercontainer.h \
$$PWD/filters/rolefilter.h \
$$PWD/filters/valuefilter.h \
$$PWD/filters/indexfilter.h \
$$PWD/filters/regexpfilter.h \
$$PWD/filters/rangefilter.h \
$$PWD/filters/expressionfilter.h \
$$PWD/filters/filtercontainerfilter.h \
$$PWD/filters/anyoffilter.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 \
$$PWD/proxyroles/proxyrole.h \
$$PWD/proxyroles/proxyrolecontainer.h \
$$PWD/proxyroles/joinrole.h \
$$PWD/proxyroles/switchrole.h \
$$PWD/proxyroles/expressionrole.h

SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \
$$PWD/filters/filter.cpp \
$$PWD/filters/filtercontainer.cpp \
$$PWD/filters/rolefilter.cpp \
$$PWD/filters/valuefilter.cpp \
$$PWD/filters/indexfilter.cpp \
$$PWD/filters/regexpfilter.cpp \
$$PWD/filters/rangefilter.cpp \
$$PWD/filters/expressionfilter.cpp \
$$PWD/filters/filtercontainerfilter.cpp \
$$PWD/filters/anyoffilter.cpp \
$$PWD/filters/alloffilter.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 \
$$PWD/proxyroles/proxyrole.cpp \
$$PWD/proxyroles/proxyrolecontainer.cpp \
$$PWD/proxyroles/joinrole.cpp \
$$PWD/proxyroles/switchrole.cpp \
$$PWD/proxyroles/expressionrole.cpp \
$$PWD/proxyroles/proxyrolesqmltypes.cpp
25 changes: 25 additions & 0 deletions libs/qmlsortfilterproxymodel/filters/alloffilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "alloffilter.h"

namespace qqsfpm {

/*!
\qmltype AllOf
\inherits Filter
\inqmlmodule SortFilterProxyModel
\brief Filter container accepting rows accepted by all its child filters
The AllOf type is a \l Filter container that accepts rows if all of its contained (and enabled) filters accept them, or if it has no filter.
Using it as a top level filter has the same effect as putting all its child filters as top level filters. It can however be usefull to use an AllOf filter when nested in an AnyOf filter.
*/
bool AllOfFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const
{
//return true if all filters return false, or if there is no filter.
return std::all_of(m_filters.begin(), m_filters.end(),
[&sourceIndex, &proxyModel] (Filter* filter) {
return filter->filterAcceptsRow(sourceIndex, proxyModel);
}
);
}

}
20 changes: 20 additions & 0 deletions libs/qmlsortfilterproxymodel/filters/alloffilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef ALLOFFILTER_H
#define ALLOFFILTER_H

#include "filtercontainerfilter.h"

namespace qqsfpm {

class AllOfFilter : public FilterContainerFilter {
Q_OBJECT

public:
using FilterContainerFilter::FilterContainerFilter;

protected:
bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override;
};

}

#endif // ALLOFFILTER_H
46 changes: 46 additions & 0 deletions libs/qmlsortfilterproxymodel/filters/anyoffilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "anyoffilter.h"

namespace qqsfpm {

/*!
\qmltype AnyOf
\inherits Filter
\inqmlmodule SortFilterProxyModel
\brief Filter container accepting rows accepted by at least one of its child filters
The AnyOf type is a \l Filter container that accepts rows if any of its contained (and enabled) filters accept them.
In the following example, only the rows where the \c firstName role or the \c lastName role match the text entered in the \c nameTextField will be accepted :
\code
TextField {
id: nameTextField
}
SortFilterProxyModel {
sourceModel: contactModel
filters: AnyOf {
RegExpFilter {
roleName: "lastName"
pattern: nameTextField.text
caseSensitivity: Qt.CaseInsensitive
}
RegExpFilter {
roleName: "firstName"
pattern: nameTextField.text
caseSensitivity: Qt.CaseInsensitive
}
}
}
\endcode
*/
bool AnyOfFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const
{
//return true if any of the enabled filters return true
return std::any_of(m_filters.begin(), m_filters.end(),
[&sourceIndex, &proxyModel] (Filter* filter) {
return filter->enabled() && filter->filterAcceptsRow(sourceIndex, proxyModel);
}
);
}

}
20 changes: 20 additions & 0 deletions libs/qmlsortfilterproxymodel/filters/anyoffilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef ANYOFFILTER_H
#define ANYOFFILTER_H

#include "filtercontainerfilter.h"

namespace qqsfpm {

class AnyOfFilter : public FilterContainerFilter {
Q_OBJECT

public:
using FilterContainerFilter::FilterContainerFilter;

protected:
bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override;
};

}

#endif // ANYOFFILTER_H
124 changes: 124 additions & 0 deletions libs/qmlsortfilterproxymodel/filters/expressionfilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "expressionfilter.h"
#include "qqmlsortfilterproxymodel.h"
#include <QtQml>

namespace qqsfpm {

/*!
\qmltype ExpressionFilter
\inherits Filter
\inqmlmodule SortFilterProxyModel
\brief Filters row with a custom filtering
An ExpressionFilter is a \l Filter allowing to implement custom filtering based on a javascript expression.
*/

/*!
\qmlproperty expression ExpressionFilter::expression
An expression to implement custom filtering, it must evaluate to a boolean.
It has the same syntax has a \l {http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html} {Property Binding} except it will be evaluated for each of the source model's rows.
Rows that have their expression evaluating to \c true will be accepted by the model.
Data for each row is exposed like for a delegate of a QML View.
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& ExpressionFilter::expression() const
{
return m_scriptString;
}

void ExpressionFilter::setExpression(const QQmlScriptString& scriptString)
{
if (m_scriptString == scriptString)
return;

m_scriptString = scriptString;
updateExpression();

Q_EMIT expressionChanged();
invalidate();
}

void ExpressionFilter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel)
{
updateContext(proxyModel);
}

bool ExpressionFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const
{
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 variantResult = expression.evaluate();

if (expression.hasError()) {
qWarning() << expression.error();
return true;
}
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 true;
}
}
return true;
}

void ExpressionFilter::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 ExpressionFilter::updateExpression()
{
if (!m_context)
return;

delete m_expression;
m_expression = new QQmlExpression(m_scriptString, m_context, 0, this);
connect(m_expression, &QQmlExpression::valueChanged, this, &ExpressionFilter::invalidate);
m_expression->setNotifyOnValueChanged(true);
m_expression->evaluate();
}

}
41 changes: 41 additions & 0 deletions libs/qmlsortfilterproxymodel/filters/expressionfilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef EXPRESSIONFILTER_H
#define EXPRESSIONFILTER_H

#include "filter.h"
#include <QQmlScriptString>

class QQmlExpression;

namespace qqsfpm {

class ExpressionFilter : public Filter
{
Q_OBJECT
Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged)

public:
using Filter::Filter;

const QQmlScriptString& expression() const;
void setExpression(const QQmlScriptString& scriptString);

void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override;

protected:
bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override;

Q_SIGNALS:
void expressionChanged();

private:
void updateContext(const QQmlSortFilterProxyModel& proxyModel);
void updateExpression();

QQmlScriptString m_scriptString;
QQmlExpression* m_expression = nullptr;
QQmlContext* m_context = nullptr;
};

}

#endif // EXPRESSIONFILTER_H
Loading

0 comments on commit e2846a6

Please sign in to comment.