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