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