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