/* * Komplex Wallpaper Engine * Copyright (C) 2026 @DigitalArtifex * https://digitalartifex.dev - https://github.com/DigitalArtifex * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ #ifndef PAGINATOR_H #define PAGINATOR_H #include #include #include #include "komplex_global.h" #include "slidingcachecontroller.h" /** * @brief The PaginationNotifier class * * Qt does not support templated QObjects. Pushing it to a notifier class * that the template can inherit fixes this */ class KOMPLEX_EXPORT PaginationNotifier : public QObject { Q_OBJECT protected: explicit PaginationNotifier(QObject *parent = nullptr) : QObject(parent) {} signals: auto dataChanged() -> void; auto resultsPerPageChanged() -> void; auto totalResultsChanged() -> void; auto totalPagesChanged() -> void; auto pageChanged() -> void; auto countChanged() -> void; }; /** * @brief The Paginator class is a type agnostic pagination controller that uses * the SlidingCacheController as it's backend */ template class KOMPLEX_EXPORT Paginator : public PaginationNotifier { public: explicit Paginator(QObject *parent = nullptr) : PaginationNotifier(parent) { } /** * @brief next * * Attempts to get the next page and notifies pageChanged */ auto next() const -> void { setOffset(m_offset + m_resultsPerPage); } /** * @brief previous * * Attempts to get the previous page and notifies pageChanged */ auto previous() const -> void { setOffset(m_offset - m_resultsPerPage); } /** * @brief resultsPerPage * @return */ auto resultsPerPage() const -> qsizetype { return m_resultsPerPage; } /** * @brief setResultsPerPage * @param resultsPerPage */ auto setResultsPerPage(qsizetype resultsPerPage) -> void { QMutexLocker dataLocker(&m_dataMutex); if(resultsPerPage == m_resultsPerPage) { return; } m_resultsPerPage = resultsPerPage; Q_EMIT static_cast(this)->resultsPerPageChanged(); m_data = m_cacheController.chunk(m_offset, m_resultsPerPage); Q_EMIT static_cast(this)->dataChanged(); } /** * @brief data * @return */ auto data() const -> QList { return m_data; } /** * @brief count * @return */ auto count() const -> qsizetype { return m_data.count(); } /** * @brief at * @param index * @return */ auto at(qsizetype index) const -> T { return get(index); } /** * @brief totalResults * * The total number of results as reported by the controller * @return */ auto totalResults() -> qsizetype { return m_cacheController.count(); } /** * @brief page * Calculates the current page * @return */ auto page() const -> qsizetype { return std::floor( static_cast(m_offset) / m_resultsPerPage ); } /** * @brief totalPages * Calculates the total number of pages available * @return */ auto totalPages() const -> qsizetype { return std::ceil( static_cast(totalResults()) / m_resultsPerPage ); } protected: /** * @brief controller * Reference to the underlying cache controller * @return */ auto controller() -> SlidingCacheController* { return &m_cacheController; } /** * @brief setOffset * Sets the dataset to the specified offset * @param offset */ auto setOffset(qsizetype offset) -> void { if(offset == m_offset) { return; } m_offset = offset; if(m_offset >= totalResults()) { m_offset = totalResults() - m_resultsPerPage; } if(m_offset < 0) { m_offset = 0; } QList data = m_cacheController.chunk(m_offset, m_resultsPerPage); setData(data); Q_EMIT this->pageChanged(); } /** * @brief setData * @param data */ auto setData(const QList &data) { m_dataMutex.lock(); m_data.clear(); m_data.append(std::move(data)); m_dataMutex.unlock(); Q_EMIT this->countChanged(); Q_EMIT this->dataChanged(); } /** * @brief onControllerTotalResultsChanged * Slot used to forward totalResultsChanged signal */ auto onControllerTotalResultsChanged() -> void { Q_EMIT this->totalResultsChanged(); } /** * @brief reset * Resets the paginator and underlying controller */ auto reset() -> void { m_dataMutex.lock(); m_offset = std::numeric_limits::infinity(); m_data.clear(); controller()->reset(); m_dataMutex.unlock(); Q_EMIT totalPagesChanged(); Q_EMIT dataChanged(); Q_EMIT pageChanged(); } private: /** * @brief get * Private thread-safe get operation. This is needed to preserve the constness * of the at function * @param index * @return */ auto get(qsizetype index) -> T { QMutexLocker dataLocker(&m_dataMutex); if(index < 0 || index >= m_data.count()) { return {}; } return m_data.at(index); } /** * @brief m_offset * Current absolute offset. Used in fetch operations */ qsizetype m_offset = std::numeric_limits::infinity(); /** * @brief m_resultsPerPage * Results per page to use when calculating current and total pages. * Needs to be set by the view */ qsizetype m_resultsPerPage = 0; /** * @brief m_cacheController * Internal cache controller. * * TODO: make the cache controller generic so it can be used in other projects * and with other cache methods */ SlidingCacheController m_cacheController; /** * @brief m_data * Internal data copy */ QList m_data; /** * @brief m_dataMutex */ QMutex m_dataMutex; }; #endif // PAGINATOR_H