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
qqmlfunctionsorter.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/qqmlfunctionsorter_p.h>
6#include <QtQmlModels/private/qqmlsortfilterproxymodel_p.h>
7#include <QtQml/private/qqmlobjectcreator_p.h>
8#include <QtQml/qqmlinfo.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \qmltype FunctionSorter
14 \inherits Sorter
15 \inqmlmodule QtQml.Models
16 \since 6.10
17 \preliminary
18 \brief Sorts data in a \l SortFilterProxyModel based on the evaluation of
19 the designated 'compare' method.
20
21 FunctionSorter allows user to define the designated 'compare' method and it
22 will be evaluated to sort the data. The method takes two arguments
23 (lhs and rhs) of the specified parameter type and the data can
24 be accessed as below for evaluation,
25
26 \qml
27 SortFilterProxyModel {
28 sourceModel: model
29 sorters: [
30 FunctionSorter {
31 id: functionSorter
32 component RoleData: QtObject {
33 property real age
34 }
35 function compare(lhsData: RoleData, rhsData: RoleData) : int {
36 return (lhsData.age < rhsData.age) ? -1 : ((lhsData === rhsData.age) ? 0 : 1)
37 }
38 }
39 ]
40 }
41 \endqml
42
43 \note \deprecated [6.12] The \c sort method is deprecated. Rename it to \c compare.
44
45 \note The user needs to explicitly invoke
46 \l{SortFilterProxyModel::invalidateSorter} whenever any external qml
47 property used within the designated 'compare' method changes. This behaviour
48 is subject to change in the future, like implicit invalidation and thus the
49 user doesn't need to explicitly invoke
50 \l{SortFilterProxyModel::invalidateSorter}.
51*/
52
53QQmlFunctionSorter::QQmlFunctionSorter(QObject *parent)
54 : QQmlSorterBase (new QQmlFunctionSorterPrivate, parent)
55{
56}
57
58QQmlFunctionSorter::~QQmlFunctionSorter()
59{
60 Q_D(QQmlFunctionSorter);
61 if (d->m_lhsParameterData.metaType().flags() & QMetaType::PointerToQObject)
62 delete d->m_lhsParameterData.value<QObject *>();
63 if (d->m_rhsParameterData.metaType().flags() & QMetaType::PointerToQObject)
64 delete d->m_rhsParameterData.value<QObject *>();
65}
66
67void QQmlFunctionSorter::componentComplete()
68{
69 Q_D(QQmlFunctionSorter);
70 const auto *metaObj = this->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() == "compare") {
75 d->m_method = method;
76 break;
77 } else if (method.nameView() == "sort") {
78 qmlWarning(this) << "The 'sort' method is deprecated and will be removed in a future version. Rename it to 'compare'.";
79 d->m_method = method;
80 break;
81 }
82 }
83
84 if (!d->m_method.isValid())
85 return;
86
87 if (d->m_method.parameterCount() != 2) {
88 qmlWarning(this) << d->m_method.name() << " requires two parameters.";
89 return;
90 }
91
92 QQmlData *data = QQmlData::get(this);
93 if (!data || !data->outerContext) {
94 qmlWarning(this) << d->m_method.name() << " requires a QML context.";
95 return;
96 }
97
98 const QMetaType parameterType = d->m_method.parameterMetaType(0);
99 if (parameterType != d->m_method.parameterMetaType(1)) {
100 qmlWarning(this) << d->m_method.name() << " parameters need to have matching types.";
101 return;
102 }
103
104 auto cu = QQmlMetaType::obtainCompilationUnit(parameterType);
105 const QQmlType parameterQmlType = QQmlMetaType::qmlType(parameterType);
106
107 QQmlRefPointer<QQmlContextData> context = data->outerContext;
108 QQmlEngine *engine = context->engine();
109
110 // The code below creates an instance of the inline component, composite,
111 // or specific C++ QObject types. The created instance, along with the
112 // data, are passed as an arguments to the 'compare' method, which is invoked
113 // during the call to QQmlFunctionSorter::compare.
114 // To create an instance of required component types (be it inline or
115 // composite), an executable compilation unit is required, and this can be
116 // obtained by looking up via metatype in the type registry
117 // (QQmlMetaType::obtainCompilationUnit). Pass it through the QML engine to
118 // make it executable. Further, use the executable compilation unit to run
119 // an object creator and produce an instance.
120 if (parameterType.flags() & QMetaType::PointerToQObject) {
121 QObject *created0 = nullptr;
122 QObject *created1 = nullptr;
123 if (parameterQmlType.isInlineComponentType()) {
124 const auto executableCu = engine->handle()->executableCompilationUnit(std::move(cu));
125 const QString icName = parameterQmlType.elementName();
126 created0 = QQmlObjectCreator(context, executableCu, context, icName).create(
127 executableCu->inlineComponentId(icName), nullptr, nullptr,
128 QQmlObjectCreator::InlineComponent);
129 created1 = QQmlObjectCreator(context, executableCu, context, icName).create(
130 executableCu->inlineComponentId(icName), nullptr, nullptr,
131 QQmlObjectCreator::InlineComponent);
132 } else if (parameterQmlType.isComposite()) {
133 const auto executableCu = engine->handle()->executableCompilationUnit(std::move(cu));
134 created0 = QQmlObjectCreator(context, executableCu, context, QString()).create();
135 created1 = QQmlObjectCreator(context, executableCu, context, QString()).create();
136 } else {
137 created0 = parameterQmlType.metaObject()->newInstance();
138 created1 = parameterQmlType.metaObject()->newInstance();
139 }
140
141 const auto names = d->m_method.parameterNames();
142 created0->setObjectName(names[0]);
143 created1->setObjectName(names[1]);
144 d->m_lhsParameterData = QVariant::fromValue(created0);
145 d->m_rhsParameterData = QVariant::fromValue(created1);
146 } else {
147 d->m_lhsParameterData = QVariant(parameterType);
148 d->m_rhsParameterData = QVariant(parameterType);
149 }
150}
151
152/*!
153 \internal
154*/
155QPartialOrdering QQmlFunctionSorter::compare(
156 const QModelIndex& sourceLeft, const QModelIndex& sourceRight,
157 const QQmlSortFilterProxyModel *proxyModel) const
158{
159 Q_D(const QQmlFunctionSorter);
160 if (!d->m_method.isValid()
161 || !d->m_lhsParameterData.isValid()
162 || !d->m_rhsParameterData.isValid()) {
163 return QPartialOrdering::Unordered;
164 }
165
166 int retVal = 0;
167 QSortFilterProxyModelHelper::setProperties(&d->m_lhsParameterData, proxyModel, sourceLeft);
168 QSortFilterProxyModelHelper::setProperties(&d->m_rhsParameterData, proxyModel, sourceRight);
169
170 void *argv[] = {&retVal, d->m_lhsParameterData.data(), d->m_rhsParameterData.data()};
171 QMetaObject::metacall(
172 const_cast<QQmlFunctionSorter *>(this), QMetaObject::InvokeMetaMethod,
173 d->m_method.methodIndex(), argv);
174
175 return (retVal == 0)
176 ? QPartialOrdering::Equivalent
177 : ((retVal < 0) ? QPartialOrdering::Less : QPartialOrdering::Greater);
178}
179
180QT_END_NAMESPACE
181
182#include "moc_qqmlfunctionsorter_p.cpp"