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

source code of qtdeclarative/src/qmlmodels/sfpm/sorters/qqmlsortercompositor.cpp