Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlfunctionfilter.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QtQmlModels/private/qqmlfunctionfilter_p.h>
6#include <QtQmlModels/private/qqmlsortfilterproxymodel_p.h>
7#include <QtQml/private/qqmlobjectcreator_p.h>
8#include <QtQml/qjsvalue.h>
9#include <QtQml/qqmlinfo.h>
10#include <QObject>
11#include <QMetaMethod>
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \qmltype FunctionFilter
17 \inherits Filter
18 \inqmlmodule QtQml.Models
19 \since 6.10
20 \preliminary
21 \brief Filters data in a \l SortFilterProxyModel based on the evaluation
22 of the designated 'filter' method.
23
24 FunctionFilter allows user to define the designated 'filter' method and it
25 will be evaluated to filter the data. The 'filter' method takes one
26 argument and it can be defined as inline component as below:
27
28 \qml
29 SortFilterProxyModel {
30 sourceModel: model
31 filters: [
32 FunctionFilter {
33 id: functionFilter
34 property int ageLimit: 20
35 function filter(age: int) : bool {
36 return (age <= ageLimit)
37 }
38 }
39 ]
40 }
41 \endqml
42
43 \note The user needs to explicitly invoke
44 \l{SortFilterProxyModel::invalidate} whenever any external qml property
45 used within the designated 'filter' method changes. This behaviour is
46 subject to change in the future, like implicit invalidation and thus the
47 user doesn't need to explicitly invoke
48 \l{SortFilterProxyModel::invalidate}.
49*/
50
51QQmlFunctionFilter::QQmlFunctionFilter(QObject *parent)
52 : QQmlFilterBase (new QQmlFunctionFilterPrivate, parent)
53{
54}
55
56QQmlFunctionFilter::~QQmlFunctionFilter()
57{
58}
59
60void QQmlFunctionFilter::update(const QQmlSortFilterProxyModel *proxyModel)
61{
62 Q_D(QQmlFunctionFilter);
63 d->parameterCache.reset();
64 QQmlFilterBase::update(proxyModel);
65}
66
67void QQmlFunctionFilter::componentComplete()
68{
69 Q_D(QQmlFunctionFilter);
70 const auto *metaObj = metaObject();
71 for (int idx = metaObj->methodOffset(); idx < metaObj->methodCount(); idx++) {
72 // Once we find the method signature, break the loop
73 QMetaMethod method = metaObj->method(idx);
74 if (method.nameView() == "filter") {
75 d->method = method;
76 break;
77 }
78 }
79
80 if (!d->method.isValid())
81 return;
82
83 // Check if the parameter types are valid;
84 for (int index = 0; index < d->method.parameterCount(); index++) {
85 const QMetaType parameterType = d->method.parameterMetaType(index);
86 if (!parameterType.isValid()) {
87 qmlWarning(this) << "filter method parameter needs to be a QML-registered type";
88 d->method = {};
89 return;
90 }
91 }
92}
93
94/*!
95 \internal
96*/
97bool QQmlFunctionFilter::filterAcceptsRowInternal(int row, const QModelIndex& sourceParent, const QQmlSortFilterProxyModel *proxyModel) const
98{
99 Q_D(const QQmlFunctionFilter);
100 if (!d->method.isValid() ||
101 (d->parameterCache.has_value() && d->parameterCache->dataArgs.isEmpty()))
102 return true;
103
104 bool retVal = false;
105
106 if (!d->parameterCache.has_value()) {
107 QQmlFunctionFilterPrivate::ParameterCache parameterCache;
108 const auto &params = d->method.parameterNames();
109 for (int index = 0; index < params.size(); index++) {
110 const int roleId = proxyModel->itemRoleForName(QString::fromUtf8(params.at(index)));
111 if (roleId < 0) {
112 qmlWarning(this) << "Parameter specified in the filter method " << params.at(index) << " doesn't exist in the model";
113 d->parameterCache = QQmlFunctionFilterPrivate::ParameterCache{};
114 return true;
115 }
116 parameterCache.paramsInfo.append({roleId, QVariant{}, d->method.parameterMetaType(index)});
117 }
118 d->parameterCache = std::move(parameterCache);
119 // Append nullptr for future utilization of this space to return value
120 // while invoking js method (´filter´)
121 d->parameterCache->dataArgs.append(nullptr);
122 for (auto &param : d->parameterCache->paramsInfo)
123 d->parameterCache->dataArgs.append(&param.value);
124 }
125
126 d->parameterCache->dataArgs[0] = &retVal;
127
128 auto filterData = [d, proxyModel, this](int row, int column, const QModelIndex &sourceParent) {
129 int index = 0;
130 for (auto &param : d->parameterCache->paramsInfo) {
131 QVariant value = proxyModel->sourceModel()->data(proxyModel->sourceModel()->index(row, column, sourceParent),
132 param.roleId);
133 if (!value.isValid())
134 return;
135 if (value.metaType() == param.expectedType) {
136 param.value = std::move(value);
137 } else {
138 // Convert according to the JS coercion rules
139 auto *v4Engine = qmlEngine(this)->handle();
140 QV4::Scope scope(v4Engine);
141 QV4::ScopedValue jsVal(scope, v4Engine->metaTypeToJS(value.metaType(), value.constData()));
142 QVariant convertedValue(param.expectedType);
143 if (!QV4::ExecutionEngine::metaTypeFromJS(jsVal, param.expectedType, convertedValue.data())) {
144 qmlWarning(this) << "Failed to convert param " << d->method.parameterNames()[index] << " to " << param.expectedType.name();
145 return;
146 }
147 param.value = std::move(convertedValue);
148 }
149 index++;
150 }
151 QMetaObject::metacall(
152 const_cast<QQmlFunctionFilter *>(this), QMetaObject::InvokeMetaMethod,
153 d->method.methodIndex(), d->parameterCache->dataArgs.data());
154 };
155
156 if (column() > -1) {
157 filterData(row, column(), sourceParent);
158 } else {
159 const int columnCount = proxyModel->sourceModel()->columnCount(sourceParent);
160 for (int column = 0; column < columnCount; column++) {
161 filterData(row, column, sourceParent);
162 if (retVal)
163 return retVal;
164 }
165 }
166
167 return retVal;
168}
169
170QT_END_NAMESPACE
171
172#include "moc_qqmlfunctionfilter_p.cpp"