| 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/qqmlfiltercompositor_p.h> |
| 5 | #include <QtQmlModels/private/qqmlsortfilterproxymodel_p.h> |
| 6 | |
| 7 | QT_BEGIN_NAMESPACE |
| 8 | |
| 9 | Q_LOGGING_CATEGORY (lcSfpmFilterCompositor, "qt.qml.sfpmfiltercompositor" ) |
| 10 | |
| 11 | QQmlFilterCompositor::QQmlFilterCompositor(QObject *parent) |
| 12 | : QQmlFilterBase(new QQmlFilterCompositorPrivate, parent) |
| 13 | { |
| 14 | Q_D(QQmlFilterCompositor); |
| 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. This is because, there are chances that |
| 19 | // the filter can be enabled or disabled in effective filter list |
| 20 | // such as the configured role name in the filter doesn't match |
| 21 | // with any role name in the model |
| 22 | connect(sender: d->m_sfpmModel, signal: &QQmlSortFilterProxyModel::modelReset, |
| 23 | context: this, slot: &QQmlFilterCompositor::updateFilters); |
| 24 | } |
| 25 | |
| 26 | QQmlFilterCompositor::~QQmlFilterCompositor() |
| 27 | { |
| 28 | |
| 29 | } |
| 30 | |
| 31 | void QQmlFilterCompositorPrivate::init() |
| 32 | { |
| 33 | Q_Q(QQmlFilterCompositor); |
| 34 | m_sfpmModel = qobject_cast<QQmlSortFilterProxyModel *>(object: q->parent()); |
| 35 | } |
| 36 | |
| 37 | void QQmlFilterCompositor::append(QQmlListProperty<QQmlFilterBase> *filterComp, QQmlFilterBase* filter) |
| 38 | { |
| 39 | auto *filterCompositor = reinterpret_cast<QQmlFilterCompositor *> (filterComp->object); |
| 40 | filterCompositor->append(filter); |
| 41 | } |
| 42 | |
| 43 | qsizetype QQmlFilterCompositor::count(QQmlListProperty<QQmlFilterBase> *filterComp) |
| 44 | { |
| 45 | auto *filterCompositor = reinterpret_cast<QQmlFilterCompositor *> (filterComp->object); |
| 46 | return filterCompositor->count(); |
| 47 | } |
| 48 | |
| 49 | QQmlFilterBase *QQmlFilterCompositor::at(QQmlListProperty<QQmlFilterBase> *filterComp, qsizetype index) |
| 50 | { |
| 51 | auto *filterCompositor = reinterpret_cast<QQmlFilterCompositor *> (filterComp->object); |
| 52 | return filterCompositor->at(index); |
| 53 | } |
| 54 | |
| 55 | void QQmlFilterCompositor::clear(QQmlListProperty<QQmlFilterBase> *filterComp) |
| 56 | { |
| 57 | auto *filterCompositor = reinterpret_cast<QQmlFilterCompositor *> (filterComp->object); |
| 58 | filterCompositor->clear(); |
| 59 | } |
| 60 | |
| 61 | void QQmlFilterCompositor::append(QQmlFilterBase *filter) |
| 62 | { |
| 63 | if (!filter) |
| 64 | return; |
| 65 | |
| 66 | Q_D(QQmlFilterCompositor); |
| 67 | d->m_filters.append(t: filter); |
| 68 | // Connect the filter to the corresponding slot to invalidate the model |
| 69 | // and the filter cache |
| 70 | QObject::connect(sender: filter, signal: &QQmlFilterBase::invalidateModel, |
| 71 | context: d->m_sfpmModel, slot: &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(sender: filter, signal: &QQmlFilterBase::invalidateCache, |
| 76 | context: this, slot: &QQmlFilterCompositor::updateCache); |
| 77 | // Validate the filter for any precondition which can be compared with |
| 78 | // sfpm and update the filter cache accordingly |
| 79 | filter->update(d->m_sfpmModel); |
| 80 | updateCache(); |
| 81 | // Since we added new filter to the list, emit the filter changed signal |
| 82 | // for the filters that have been appended to the list |
| 83 | emit d->m_sfpmModel->filtersChanged(); |
| 84 | } |
| 85 | |
| 86 | qsizetype QQmlFilterCompositor::count() |
| 87 | { |
| 88 | Q_D(QQmlFilterCompositor); |
| 89 | return d->m_filters.count(); |
| 90 | } |
| 91 | |
| 92 | QQmlFilterBase *QQmlFilterCompositor::at(qsizetype index) |
| 93 | { |
| 94 | Q_D(QQmlFilterCompositor); |
| 95 | return d->m_filters.at(i: index); |
| 96 | } |
| 97 | |
| 98 | void QQmlFilterCompositor::clear() |
| 99 | { |
| 100 | Q_D(QQmlFilterCompositor); |
| 101 | d->m_effectiveFilters.clear(); |
| 102 | d->m_filters.clear(); |
| 103 | // Emit the filter changed signal as we cleared the filter list |
| 104 | emit d->m_sfpmModel->filtersChanged(); |
| 105 | } |
| 106 | |
| 107 | QList<QQmlFilterBase *> QQmlFilterCompositor::filters() |
| 108 | { |
| 109 | Q_D(QQmlFilterCompositor); |
| 110 | return d->m_filters; |
| 111 | } |
| 112 | |
| 113 | QQmlListProperty<QQmlFilterBase> QQmlFilterCompositor::filtersListProperty() |
| 114 | { |
| 115 | Q_D(QQmlFilterCompositor); |
| 116 | return QQmlListProperty<QQmlFilterBase>(reinterpret_cast<QObject*>(this), &d->m_filters, |
| 117 | QQmlFilterCompositor::append, |
| 118 | QQmlFilterCompositor::count, |
| 119 | QQmlFilterCompositor::at, |
| 120 | QQmlFilterCompositor::clear); |
| 121 | } |
| 122 | |
| 123 | void QQmlFilterCompositor::updateFilters() |
| 124 | { |
| 125 | Q_D(QQmlFilterCompositor); |
| 126 | // Update filters that have dependencies with the model data |
| 127 | for (auto &filter: d->m_filters) |
| 128 | filter->update(d->m_sfpmModel); |
| 129 | // Update the cache |
| 130 | updateCache(); |
| 131 | } |
| 132 | |
| 133 | void QQmlFilterCompositor::updateCache() |
| 134 | { |
| 135 | Q_D(QQmlFilterCompositor); |
| 136 | // Clear the existing cache |
| 137 | d->m_effectiveFilters.clear(); |
| 138 | if (d->m_sfpmModel && d->m_sfpmModel->sourceModel()) { |
| 139 | QList<QQmlFilterBase *> filters = d->m_filters; |
| 140 | // Cache only the filters that need to be evaluated (in order) |
| 141 | std::copy_if(first: filters.begin(), last: filters.end(), result: std::back_inserter(x&: d->m_effectiveFilters), |
| 142 | pred: [](QQmlFilterBase *filter){ return filter->enabled(); }); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | bool QQmlFilterCompositor::filterAcceptsRowInternal(int row, const QModelIndex& sourceParent, const QQmlSortFilterProxyModel *proxyModel) const |
| 147 | { |
| 148 | Q_D(const QQmlFilterCompositor); |
| 149 | // Check the data against the configured filters and if nothing configured, |
| 150 | // dont filter the data |
| 151 | return std::all_of(first: d->m_effectiveFilters.begin(), last: d->m_effectiveFilters.end(), |
| 152 | pred: [row, &sourceParent, proxyModel](const QQmlFilterBase *filter) { |
| 153 | const bool filterStatus = filter->filterAcceptsRowInternal(row, sourceParent, proxyModel); |
| 154 | return !(filter->invert()) ? filterStatus : !filterStatus; |
| 155 | }); |
| 156 | } |
| 157 | |
| 158 | bool QQmlFilterCompositor::filterAcceptsColumnInternal(int column, const QModelIndex& sourceParent, const QQmlSortFilterProxyModel *proxyModel) const |
| 159 | { |
| 160 | Q_D(const QQmlFilterCompositor); |
| 161 | // Check the data against the configured filters and if nothing configured, |
| 162 | // dont filter the data |
| 163 | return std::all_of(first: d->m_effectiveFilters.begin(), last: d->m_effectiveFilters.end(), |
| 164 | pred: [column, &sourceParent, proxyModel](const QQmlFilterBase *filter) { |
| 165 | return filter->filterAcceptsColumnInternal(column, sourceParent, proxyModel); |
| 166 | }); |
| 167 | } |
| 168 | |
| 169 | QT_END_NAMESPACE |
| 170 | |
| 171 | #include "moc_qqmlfiltercompositor_p.cpp" |
| 172 | |