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
qqmlsortercompositor.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/qqmlsortercompositor_p.h>
6#include <QtQmlModels/private/qqmlsortfilterproxymodel_p_p.h>
7#include <QtQmlModels/private/qqmlsortfilterproxymodel_p.h>
8
9QT_BEGIN_NAMESPACE
10
11QQmlSorterCompositor::QQmlSorterCompositor(QObject *parent)
12 : QQmlSorterBase(new QQmlSorterCompositorPrivate, parent)
13{
14 Q_D(QQmlSorterCompositor);
15 d->init();
16 // Connect the model reset with the update in the filter
17 // The cache need to be updated once the model is reset with the
18 // source model data.
19 connect(d->m_sfpmModel, &QQmlSortFilterProxyModel::modelReset,
20 this, &QQmlSorterCompositor::updateSorters);
21 connect(d->m_sfpmModel, &QQmlSortFilterProxyModel::primarySorterChanged,
22 this, &QQmlSorterCompositor::updateEffectiveSorters);
23}
24
25QQmlSorterCompositor::~QQmlSorterCompositor()
26{
27
28}
29
30void QQmlSorterCompositorPrivate::init()
31{
32 Q_Q(QQmlSorterCompositor);
33 m_sfpmModel = qobject_cast<QQmlSortFilterProxyModel *>(q->parent());
34}
35
36void QQmlSorterCompositor::append(QQmlListProperty<QQmlSorterBase> *sorterComp, QQmlSorterBase* sorter)
37{
38 auto *sorterCompositor = reinterpret_cast<QQmlSorterCompositor *>(sorterComp->object);
39 sorterCompositor->append(sorter);
40}
41
42qsizetype QQmlSorterCompositor::count(QQmlListProperty<QQmlSorterBase> *sorterComp)
43{
44 auto *sorterCompositor = reinterpret_cast<QQmlSorterCompositor *> (sorterComp->object);
45 return sorterCompositor->count();
46}
47
48QQmlSorterBase *QQmlSorterCompositor::at(QQmlListProperty<QQmlSorterBase> *sorterComp, qsizetype index)
49{
50 auto *sorterCompositor = reinterpret_cast<QQmlSorterCompositor *> (sorterComp->object);
51 return sorterCompositor->at(index);
52}
53
54void QQmlSorterCompositor::clear(QQmlListProperty<QQmlSorterBase> *sorterComp)
55{
56 auto *sorterCompositor = reinterpret_cast<QQmlSorterCompositor *> (sorterComp->object);
57 sorterCompositor->clear();
58}
59
60void QQmlSorterCompositor::append(QQmlSorterBase *sorter)
61{
62 if (!sorter)
63 return;
64 Q_D(QQmlSorterCompositor);
65 d->m_sorters.append(sorter);
66 // Update sorter cache depending on the priority
67 updateCache();
68 // Connect the sorter to the corresponding slot to invalidate the model
69 // and the sorter cache
70 QObject::connect(sorter, &QQmlSorterBase::invalidateModel,
71 d->m_sfpmModel, &QQmlSortFilterProxyModel::invalidate);
72 // This is needed as its required to update cache when there is any
73 // change in the filter itself (for instance, a change in the priority of
74 // the filter)
75 QObject::connect(sorter, &QQmlSorterBase::invalidateCache,
76 this, &QQmlSorterCompositor::updateCache);
77 // Reset the primary sort column when any sort order or column
78 // changed
79 QObject::connect(sorter, &QQmlSorterBase::sortOrderChanged,
80 this, [d] { d->resetPrimarySorter(); });
81 QObject::connect(sorter, &QQmlSorterBase::columnChanged,
82 this, [d] { d->resetPrimarySorter(); });
83 // Since we added new filter to the list, emit the filter changed signal
84 // for the filters thats been appended to the list
85 emit d->m_sfpmModel->sortersChanged();
86}
87
88qsizetype QQmlSorterCompositor::count()
89{
90 Q_D(QQmlSorterCompositor);
91 return d->m_sorters.count();
92}
93
94QQmlSorterBase* QQmlSorterCompositor::at(qsizetype index)
95{
96 Q_D(QQmlSorterCompositor);
97 return d->m_sorters.at(index);
98}
99
100void QQmlSorterCompositor::clear()
101{
102 Q_D(QQmlSorterCompositor);
103 d->m_effectiveSorters.clear();
104 d->m_sorters.clear();
105 // Emit the filter changed signal as we cleared the filter list
106 emit d->m_sfpmModel->sortersChanged();
107}
108
109QList<QQmlSorterBase *> QQmlSorterCompositor::sorters()
110{
111 Q_D(QQmlSorterCompositor);
112 return d->m_sorters;
113}
114
115QQmlListProperty<QQmlSorterBase> QQmlSorterCompositor::sortersListProperty()
116{
117 Q_D(QQmlSorterCompositor);
118 return QQmlListProperty<QQmlSorterBase>(reinterpret_cast<QObject*>(this), &d->m_sorters,
119 QQmlSorterCompositor::append,
120 QQmlSorterCompositor::count,
121 QQmlSorterCompositor::at,
122 QQmlSorterCompositor::clear);
123}
124
125void QQmlSorterCompositor::updateEffectiveSorters()
126{
127 Q_D(QQmlSorterCompositor);
128
129 if (!d->m_primarySorter || !d->m_primarySorter->enabled()) {
130 updateCache();
131 return;
132 }
133
134 QList<QQmlSorterBase *> sorters;
135 sorters.append(d->m_primarySorter);
136 std::copy_if(d->m_effectiveSorters.constBegin(), d->m_effectiveSorters.constEnd(),
137 std::back_inserter(sorters), [d](QQmlSorterBase *sorter){
138 // Consider only the filters that are enabled and exclude the primary
139 // sorter as its already added to the list
140 return (sorter != d->m_primarySorter);
141 });
142 d->m_effectiveSorters = sorters;
143}
144
145void QQmlSorterCompositor::updateSorters()
146{
147 Q_D(QQmlSorterCompositor);
148 // Update sorters that has dependency with the model data to determine
149 // whether it needs to be included or not
150 for (auto &sorter: d->m_sorters)
151 sorter->update(d->m_sfpmModel);
152 updateCache();
153}
154
155void QQmlSorterCompositor::updateCache()
156{
157 Q_D(QQmlSorterCompositor);
158 // Clear the existing cache
159 d->m_effectiveSorters.clear();
160 if (d->m_sfpmModel && d->m_sfpmModel->sourceModel()) {
161 // Sort the filter according to their priority
162 QList<QQmlSorterBase *> sorters = d->m_sorters;
163 std::stable_sort(sorters.begin(), sorters.end(),
164 [](QQmlSorterBase *sorterLeft, QQmlSorterBase *sorterRight) {
165 return sorterLeft->priority() < sorterRight->priority();
166 });
167 // Cache only the filters that are need to be evaluated (in order)
168 std::copy_if(sorters.begin(), sorters.end(), std::back_inserter(d->m_effectiveSorters),
169 [](QQmlSorterBase *sorter) { return sorter->enabled(); });
170 // If there is no primary sorter set by the user explicitly, reset the
171 // primary sorter according to the sorters in the lists
172 d->resetPrimarySorter();
173 }
174}
175
176bool QQmlSorterCompositor::lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel *proxyModel) const
177{
178 Q_D(const QQmlSorterCompositor);
179 for (const auto &sorter : d->m_effectiveSorters) {
180 const int sortSection = sorter->column();
181 if ((sortSection > -1) && (sortSection < proxyModel->sourceModel()->columnCount())) {
182 const auto *sourceModel = proxyModel->sourceModel();
183 const QPartialOrdering result = sorter->compare(sourceModel->index(sourceLeft.row(), sortSection),
184 sourceModel->index(sourceRight.row(), sortSection),
185 proxyModel);
186 if (result == QPartialOrdering::Less || result == QPartialOrdering::Greater) {
187 auto *priv = static_cast<const QQmlSortFilterProxyModelPrivate *>(QQmlSortFilterProxyModelPrivate::get(proxyModel));
188 return (priv->m_sortOrder == sorter->sortOrder()) ? result < 0 : result > 0;
189 }
190 }
191 }
192 // Verify the index order when the ordering is either equal or unordered
193 return sourceLeft.row() < sourceRight.row();
194}
195
196void QQmlSorterCompositorPrivate::setPrimarySorter(QQmlSorterBase *sorter)
197{
198 if (sorter == nullptr ||
199 (std::find(m_sorters.constBegin(), m_sorters.constEnd(), sorter) != m_sorters.constEnd())) {
200 m_primarySorter = sorter;
201 if (m_primarySorter && m_primarySorter->enabled()) {
202 m_sfpmModel->setPrimarySortOrder(m_primarySorter->sortOrder());
203 m_sfpmModel->setPrimarySortColumn(m_primarySorter->column());
204 return;
205 }
206 }
207 resetPrimarySorter();
208}
209
210void QQmlSorterCompositorPrivate::resetPrimarySorter()
211{
212 if (!m_primarySorter) {
213 if (!m_effectiveSorters.isEmpty()) {
214 // Set the primary sort column and its order to the proxy model
215 const auto *sorter = m_effectiveSorters.at(0);
216 m_sfpmModel->setPrimarySortOrder(sorter->sortOrder());
217 m_sfpmModel->setPrimarySortColumn(sorter->column());
218 } else {
219 // By default reset the sort order to ascending order and the
220 // column to 0
221 m_sfpmModel->setPrimarySortOrder(Qt::AscendingOrder);
222 m_sfpmModel->setPrimarySortColumn(0);
223 }
224 }
225}
226
227QT_END_NAMESPACE
228
229#include "moc_qqmlsortercompositor_p.cpp"