General update

This commit is contained in:
Digital Artifex
2026-06-22 01:59:33 -04:00
parent 7854a3f440
commit 5a150c29f9
44 changed files with 2397 additions and 835 deletions
+5 -3
View File
@@ -19,9 +19,7 @@ qt_add_qml_module(
newestpacksmodel.cpp
common/komplex_global.h
common/slidingcachecontroller.h
common/paginator.h
common/coreservices.h
common/coreservices.cpp
common/logging.h
@@ -30,7 +28,7 @@ qt_add_qml_module(
SOURCES paginators/featuredpackspaginator.h
SOURCES paginators/featuredimagespaginator.h
SOURCES paginators/wallpaperpaginator.h
SOURCES paginators/searchpackspaginator.h
SOURCES paginators/packsearchpaginator.h
SOURCES paginators/imagesearchpaginator.h
SOURCES paginators/newestshaderspaginator.h
SOURCES paginators/newestcubemapspaginator.h
@@ -54,6 +52,10 @@ qt_add_qml_module(
SOURCES imagesearchmodel.h
SOURCES videosearchmodel.h
SOURCES videosearchmodel.cpp
SOURCES
SOURCES
SOURCES packsearchmodel.cpp
SOURCES packsearchmodel.h
)
+2
View File
@@ -18,6 +18,7 @@ struct KOMPLEX_EXPORT ImageCache
QString photographerUrl;
qint64 photographerId;
QMap<QString, QString> sources;
QMap<QString, QString> sourceSizes;
QString url;
auto operator == (const ImageCache &other) const -> bool
@@ -33,6 +34,7 @@ struct KOMPLEX_EXPORT ImageCache
other.photographerUrl == photographerUrl &&
other.photographerId == photographerId &&
other.sources == sources &&
other.sourceSizes == sourceSizes &&
other.url == url
);
}
+5
View File
@@ -18,6 +18,8 @@
*/
#ifndef KOMPLEX_GLOBAL_H
#define KOMPLEX_GLOBAL_H
#include <QtTypes>
#include <chrono>
#ifdef KOMPLEX_PLUGIN
#define KOMPLEX_EXPORT Q_DECL_EXPORT
@@ -61,4 +63,7 @@
#define KOMPLEX_ENDPOINT_PACKS_ITEM "packs/item"
#define KOMPLEX_ENDPOINT_PACKS_NEWEST "packs/newest"
inline static qsizetype nullsize = std::numeric_limits<qsizetype>::min();
inline static std::chrono::milliseconds KOMPLEX_RATE_LIMIT(500);
#endif // KOMPLEX_GLOBAL_H
+134 -61
View File
@@ -39,12 +39,14 @@ class KOMPLEX_EXPORT PaginationNotifier : public QObject
protected:
explicit PaginationNotifier(QObject *parent = nullptr) : QObject(parent) {}
signals:
auto dataChanged() -> void;
auto fetchComplete() -> void;
auto fetching() -> void;
auto resultsPerPageChanged() -> void;
auto totalResultsChanged() -> void;
auto totalPagesChanged() -> void;
auto pageChanged() -> void;
auto countChanged() -> void;
auto queryChanged() -> void;
};
/**
@@ -57,7 +59,21 @@ class KOMPLEX_EXPORT Paginator : public PaginationNotifier
public:
explicit Paginator(QObject *parent = nullptr) : PaginationNotifier(parent)
{
QObject::connect
(
&m_cacheController,
&SlidingCacheNotifier::fetchComplete,
this,
&PaginationNotifier::fetchComplete
);
QObject::connect
(
&m_cacheController,
&SlidingCacheNotifier::fetching,
this,
&PaginationNotifier::fetching
);
}
/**
@@ -67,7 +83,15 @@ public:
*/
auto next() -> void
{
setOffset(m_offset + m_resultsPerPage);
if(m_offset != nullsize)
{
setOffset(m_offset + m_resultsPerPage);
}
else
{
controller()->init();
setOffset(0);
}
}
/**
@@ -77,7 +101,15 @@ public:
*/
auto previous() -> void
{
setOffset(m_offset - m_resultsPerPage);
if(m_offset != nullsize && m_offset > m_resultsPerPage)
{
setOffset(m_offset - m_resultsPerPage);
}
else
{
controller()->init();
setOffset(0);
}
}
/**
@@ -95,8 +127,6 @@ public:
*/
auto setResultsPerPage(qsizetype resultsPerPage) -> void
{
QMutexLocker dataLocker(&m_dataMutex);
if(resultsPerPage == m_resultsPerPage)
{
return;
@@ -104,18 +134,53 @@ public:
m_resultsPerPage = resultsPerPage;
Q_EMIT static_cast<PaginationNotifier*>(this)->resultsPerPageChanged();
}
m_data = m_cacheController.chunk(m_offset, m_resultsPerPage);
Q_EMIT static_cast<PaginationNotifier*>(this)->dataChanged();
/**
* @brief query
* Current query string, if the API Endpoint supports queries
* @return
*/
[[nodiscard]]
auto query() const -> QString
{
return m_query;
}
/**
* @brief setQuery
* Sets the new query string and updates the controller
* @param query
*/
auto setQuery(const QString &query) -> void
{
if(query == m_query)
{
return;
}
reset();
m_query = query;
Q_EMIT queryChanged();
controller()->init();
setOffset(0);
}
/**
* @brief data
* @return
*/
auto data() const -> QList<T>
auto data() const -> const QList<T>
{
return m_data;
if(boundaryCheck(m_resultsPerPage))
{
return m_cacheController.chunk(m_offset, m_resultsPerPage);
}
qsizetype count = (m_cacheController.totalCount() - m_offset);
return m_cacheController.chunk(m_offset, count);
}
/**
@@ -124,7 +189,18 @@ public:
*/
auto count() const -> qsizetype
{
return m_data.count();
if(m_offset == nullsize)
{
return 0;
}
if(boundaryCheck(m_resultsPerPage))
{
return m_resultsPerPage;
}
qsizetype count = (m_cacheController.totalCount() - m_offset);
return count;
}
/**
@@ -132,16 +208,9 @@ public:
* @param index
* @return
*/
auto at(qsizetype index) const -> T
auto at(qsizetype index) const -> const T&
{
QMutexLocker dataLocker(&m_dataMutex);
if(index < 0 || index >= m_data.count())
{
return {};
}
return m_data.at(index);
return m_cacheController.at(m_offset + index);
}
/**
@@ -181,6 +250,16 @@ public:
protected:
auto queryable() const -> bool
{
return m_queryable;
}
auto setQueryable(bool queryable) -> void
{
m_queryable = queryable;
}
/**
* @brief controller
* Reference to the underlying cache controller
@@ -198,16 +277,11 @@ protected:
*/
auto setOffset(qsizetype offset) -> void
{
if(offset == m_offset && offset != std::numeric_limits<qsizetype>::infinity())
if(offset == m_offset)
{
return;
}
if(offset == std::numeric_limits<qsizetype>::infinity())
{
offset = 0;
}
m_offset = offset;
if(m_offset >= totalResults())
@@ -220,26 +294,11 @@ protected:
m_offset = 0;
}
QList<T> data = m_cacheController.chunk(m_offset, m_resultsPerPage);
setData(data);
m_cacheController.focus(offset, m_resultsPerPage);
Q_EMIT this->pageChanged();
}
/**
* @brief setData
* @param data
*/
auto setData(const QList<T> &data)
{
m_dataMutex.lock();
m_data.clear();
m_data.append(std::move(data));
m_dataMutex.unlock();
Q_EMIT this->countChanged();
Q_EMIT this->dataChanged();
Q_EMIT totalResultsChanged();
Q_EMIT totalPagesChanged();
Q_EMIT pageChanged();
}
/**
@@ -257,26 +316,34 @@ protected:
*/
auto reset() -> void
{
m_dataMutex.lock();
m_data.clear();
m_query.clear();
m_offset = nullsize;
controller()->reset();
m_dataMutex.unlock();
Q_EMIT totalPagesChanged();
Q_EMIT dataChanged();
Q_EMIT pageChanged();
setOffset(std::numeric_limits<qsizetype>::infinity());
}
private:
/**
* @brief boundaryCheck
* Checks if the index is within the bounds of the dataset
* @param index
* @return
*/
auto boundaryCheck(qsizetype index) const -> bool
{
if(index < 0 || (m_offset + index) >= m_cacheController.totalCount())
{
return false;
}
return true;
}
/**
* @brief m_offset
* Current absolute offset. Used in fetch operations
*/
qsizetype m_offset = std::numeric_limits<qsizetype>::infinity();
mutable qsizetype m_offset = nullsize;
/**
* @brief m_resultsPerPage
@@ -294,16 +361,22 @@ private:
*/
SlidingCacheController<T> m_cacheController;
/**
* @brief m_data
* Internal data copy
*/
QList<T> m_data;
/**
* @brief m_dataMutex
*/
mutable QMutex m_dataMutex;
/**
* @brief m_query
* The query string used in fetch operations
*/
QString m_query;
/**
* @brief m_queryable
* Controls the queryable state of the paginator
*/
bool m_queryable = false;
};
#endif // PAGINATOR_H
+168 -42
View File
@@ -23,6 +23,7 @@
#include <QList>
#include <QMutex>
#include <QMutexLocker>
#include <QThread>
#include <functional>
#include "fetchresult.h"
@@ -40,6 +41,9 @@ class KOMPLEX_EXPORT SlidingCacheNotifier : public QObject
Q_OBJECT
signals:
auto countChanged() -> void;
auto fetching() -> void;
auto fetchComplete() -> void;
auto windowSizeChanged() -> void;
};
/**
@@ -50,6 +54,12 @@ signals:
template <typename T>
class KOMPLEX_EXPORT SlidingCacheController : public SlidingCacheNotifier
{
/**
* @brief FetchFunction
* Convenience def used by setFetch
*/
typedef std::function<FetchResult<T> (qsizetype offset, qsizetype limit)> FetchFunction;
public:
explicit SlidingCacheController() : SlidingCacheNotifier{}
@@ -70,9 +80,7 @@ public:
* QObject
* @param fetch
*/
auto setFetch(
const std::function<FetchResult<T> (qsizetype offset, qsizetype limit)> &fetch
) -> void
auto setFetch(const FetchFunction &fetch) -> void
{
m_fetch = std::move(fetch);
}
@@ -85,25 +93,26 @@ public:
* @return CacheObject* or nullptr
*/
[[nodiscard]]
auto at(qsizetype index) const -> T
auto at(qsizetype index) const -> const T&
{
QMutexLocker cacheLocker(&m_cacheMutex);
T defaultValue;
if(!externalBoundaryCheck(index))
{
return {};
return defaultValue;
}
if(!const_cast<SlidingCacheController*>(this)->slideWindow(index))
{
return {};
return defaultValue;
}
qsizetype internalIndex = this->internalIndex(index);
if(!internalBoundaryCheck(index))
{
return {};
return defaultValue;
}
return m_cache.at(index);
@@ -144,19 +153,7 @@ public:
count = m_cache.count() - index;
}
QList<T> dataChunk;
for(qsizetype i = index; (i - index) < count; ++i)
{
T data = m_cache.at(i);
if(data != T{})
{
dataChunk.append(data);
}
}
return dataChunk;
return m_cache.mid(index, count);
}
/**
@@ -167,7 +164,7 @@ public:
[[nodiscard]]
auto totalCount() const -> qsizetype
{
if(m_totalCount == std::numeric_limits<qsizetype>::infinity())
if(m_totalCount == nullsize)
{
return 0;
}
@@ -195,10 +192,53 @@ public:
QMutexLocker cacheLocker(&m_cacheMutex);
m_cache.clear();
setTotalCount(std::numeric_limits<qsizetype>::infinity());
setTotalCount(nullsize);
m_windowOffset = 0;
}
auto focus(qsizetype index, qsizetype count) -> void
{
if(inFocus(index, count) || !externalBoundaryCheck(index))
{
return;
}
if(!externalBoundaryCheck(index + count))
{
slideWindow(index);
}
}
auto init() -> bool
{
if(m_totalCount != nullsize)
{
return false;
}
return slideForward();
}
auto windowSize() const -> qsizetype
{
if(m_windowSize == nullsize)
{
return SLIDING_CACHE_WINDOW_SIZE;
}
return m_windowSize;
}
auto setWindowSize(qsizetype windowSize) -> void
{
if(windowSize == m_windowSize)
{
return;
}
}
protected:
/**
* @brief setTotalCount
@@ -215,6 +255,16 @@ protected:
}
private:
auto inFocus(qsizetype index, qsizetype count) -> bool
{
qsizetype internalIndex = this->internalIndex(index);
return
(
internalBoundaryCheck(internalIndex, internalIndex + count)
);
}
/**
* @brief externalBoundaryCheck
*
@@ -224,12 +274,29 @@ private:
* @return True if index is within the external dataset boundary
* OR an initial fetch has not been performed.
*/
auto externalBoundaryCheck(qsizetype index) const -> bool
template<typename... Ts>
auto externalBoundaryCheck(Ts... index) const -> bool
{
return (
static_assert
(
(std::is_same_v<Ts, qsizetype> && ...),
"All arguments must be qsizetype"
);
bool safe = false;
((safe = externalBoundaryCheckHelper(index)),...);
return safe;
}
auto externalBoundaryCheckHelper(qsizetype index) const -> bool
{
return
(
index >= 0 &&
(
m_totalCount == std::numeric_limits<qsizetype>::infinity() ||
m_totalCount == nullsize ||
index < m_totalCount
)
);
@@ -242,12 +309,18 @@ private:
* @param index
* @return True if index is within the internal dataset boundary
*/
auto internalBoundaryCheck(qsizetype index) const -> bool
template<typename... Ts>
auto internalBoundaryCheck(Ts... index) const -> bool
{
return (
index >= 0 &&
index < m_cache.count()
static_assert
(
(std::is_same_v<Ts, qsizetype> && ...),
"All arguments must be qsizetype"
);
bool safe = false;
((safe = index >= 0 && index < m_cache.count()), ...);
return safe;
}
/**
@@ -280,14 +353,6 @@ private:
}
}
if(m_totalCount == std::numeric_limits<qsizetype>::infinity())
{
if(!slideForward())
{
return false;
}
}
return true;
}
@@ -299,20 +364,41 @@ private:
auto slideForward() -> bool
{
LOG_ERROR_X(!m_fetch,
"SlidingCacheController::slideBackward",
"SlidingCacheController::slideForward",
"Fetch function not set",
false
);
Q_EMIT fetching();
qsizetype movementSize = SLIDING_CACHE_WINDOW_SIZE;
qsizetype prefetchThreshold = SLIDING_CACHE_PREFETCH_THRESHOLD;
if(m_windowSize != nullsize)
{
movementSize = m_windowSize;
if(prefetchThreshold >= m_windowSize)
{
prefetchThreshold = std::floor(
static_cast<qreal>(movementSize) * 0.8
);
}
}
LOG_ERROR_X(movementSize <= 0 || prefetchThreshold <= 0,
"SlidingCacheController::slideForward",
"Invalid movement size",
false
);
//after initial inflation we should only arrive here when the prefetch threshold is reached
if(!m_cache.isEmpty())
{
movementSize -= SLIDING_CACHE_PREFETCH_THRESHOLD;
movementSize -= prefetchThreshold;
}
if(m_totalCount != std::numeric_limits<qsizetype>::infinity())
if(m_totalCount != nullsize)
{
m_windowOffset += movementSize;
}
@@ -321,6 +407,8 @@ private:
m_windowOffset = 0;
}
QThread::sleep(KOMPLEX_RATE_LIMIT);
FetchResult<T> result = m_fetch(
m_windowOffset,
movementSize
@@ -337,6 +425,8 @@ private:
if(result.data.isEmpty())
{
m_windowOffset -= movementSize;
Q_EMIT fetchComplete();
return false;
}
@@ -359,6 +449,8 @@ private:
m_cache.append(std::move(result.data));
}
Q_EMIT fetchComplete();
return true;
}
@@ -375,7 +467,31 @@ private:
false
);
qsizetype movementSize = SLIDING_CACHE_WINDOW_SIZE - SLIDING_CACHE_PREFETCH_THRESHOLD;
Q_EMIT fetching();
qsizetype movementSize = SLIDING_CACHE_WINDOW_SIZE;
qsizetype prefetchThreshold = SLIDING_CACHE_PREFETCH_THRESHOLD;
if(m_windowSize != nullsize)
{
movementSize = m_windowSize;
if(prefetchThreshold >= m_windowSize)
{
prefetchThreshold = std::floor(
static_cast<qreal>(movementSize) * 0.8
);
}
}
LOG_ERROR_X(movementSize <= 0 || prefetchThreshold <= 0,
"SlidingCacheController::slideBackward",
"Invalid movement size",
false
);
QThread::sleep(KOMPLEX_RATE_LIMIT);
m_windowOffset -= movementSize;
if(m_windowOffset < 0)
@@ -391,6 +507,8 @@ private:
if(result.count == 0)
{
m_windowOffset += movementSize;
Q_EMIT fetchComplete();
return false;
}
@@ -419,6 +537,8 @@ private:
}
}
Q_EMIT fetchComplete();
return true;
}
@@ -444,7 +564,7 @@ private:
* Total number of results available. Data comes from the return data of
* m_fetch
*/
qsizetype m_totalCount = std::numeric_limits<qsizetype>::infinity();
qsizetype m_totalCount = nullsize;
/**
* @brief m_fetch
@@ -463,6 +583,12 @@ private:
* @brief m_cacheMutex
*/
mutable QMutex m_cacheMutex;
/**
* @brief m_windowSize
* Override for window size
*/
qsizetype m_windowSize = nullsize;
};
#endif // SLIDINGCACHECONTROLLER_H
+57 -22
View File
@@ -2,8 +2,6 @@
FeaturedImagesModel::FeaturedImagesModel(QObject *parent) : QAbstractListModel{parent}
{
setState(Loading);
m_paginator = new FeaturedImagesPaginator(this);
PaginationNotifier *notifier = static_cast<PaginationNotifier*>(m_paginator);
@@ -37,10 +35,24 @@ FeaturedImagesModel::FeaturedImagesModel(QObject *parent) : QAbstractListModel{p
QObject::connect(
notifier,
&PaginationNotifier::dataChanged,
&PaginationNotifier::pageChanged,
this,
&FeaturedImagesModel::resetDataModel
);
QObject::connect(
notifier,
&PaginationNotifier::fetchComplete,
this,
&FeaturedImagesModel::resetState
);
QObject::connect(
notifier,
&PaginationNotifier::fetching,
this,
&FeaturedImagesModel::onPaginatorFetching
);
}
auto FeaturedImagesModel::rowCount(const QModelIndex &) const -> int
@@ -55,12 +67,12 @@ auto FeaturedImagesModel::rowCount(const QModelIndex &) const -> int
auto FeaturedImagesModel::data(const QModelIndex &index, int role) const -> QVariant
{
if(index.row() < 0 || m_paginator == nullptr)
if(!boundaryCheck(index.row()))
{
return {};
}
ImageCache dataPoint = m_data.at(index.row());
ImageCache dataPoint = m_paginator->at(index.row());
QVariant data;
@@ -114,10 +126,12 @@ auto FeaturedImagesModel::index(int row, int column, const QModelIndex &parent)
{
Q_UNUSED(parent)
if(row < 0 || row >= m_data.count())
if(!boundaryCheck(row))
{
return {};
}
return createIndex(row, column, &m_data[row]);
return createIndex(row, column, &m_paginator[row]);
}
auto FeaturedImagesModel::columnCount(const QModelIndex &) const -> int
@@ -158,25 +172,28 @@ auto FeaturedImagesModel::resetState() -> void
auto FeaturedImagesModel::resetDataModel() -> void
{
setState(Loading);
//invalidate previous model data
beginRemoveRows(QModelIndex(), 0, m_data.count());
m_data.clear();
beginRemoveRows(QModelIndex(), 0, m_paginator->count());
endRemoveRows();
//signal new data
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
auto dataset = m_paginator->data();
endInsertRows();
}
for(const ImageCache& data : std::as_const(dataset))
auto FeaturedImagesModel::onPaginatorFetching() -> void
{
setState(Loading);
}
auto FeaturedImagesModel::boundaryCheck(qsizetype index) const -> bool
{
if(index < 0 || m_paginator == nullptr || index >= m_paginator->count())
{
m_data.append(data);
return false;
}
endInsertRows();
setState(Idle);
return true;
}
auto FeaturedImagesModel::roleNames() const -> QHash<int, QByteArray>
@@ -214,7 +231,13 @@ auto FeaturedImagesModel::setResultsPerPage(qsizetype resultsPerPage) -> void
{
if(m_paginator != nullptr)
{
m_paginator->setResultsPerPage(resultsPerPage);
QFuture<void> future = QtConcurrent::run
(
[this, resultsPerPage]
{
m_paginator->setResultsPerPage(resultsPerPage);
}
);
}
}
@@ -228,19 +251,31 @@ auto FeaturedImagesModel::totalResults() const -> qsizetype
return 0;
}
auto FeaturedImagesModel::nextPage() const -> void
auto FeaturedImagesModel::nextPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->next();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->next();
}
);
}
}
auto FeaturedImagesModel::previousPage() const -> void
auto FeaturedImagesModel::previousPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->previous();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->previous();
}
);
}
}
+26 -4
View File
@@ -35,6 +35,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QtConcurrent/QtConcurrentRun>
#include "paginators/featuredimagespaginator.h"
@@ -200,13 +201,13 @@ public:
* @brief nextPage
* Function for the QML frontend
*/
Q_INVOKABLE auto nextPage() const -> void;
Q_INVOKABLE auto nextPage() -> void;
/**
* @brief previousPage
* Function for the QML frontend
*/
Q_INVOKABLE auto previousPage() const -> void;
Q_INVOKABLE auto previousPage() -> void;
protected:
/**
@@ -226,8 +227,19 @@ protected:
*/
auto resetState() -> void;
/**
* @brief resetDataModel
* Clears the data model and creates a new one if data exists
*/
auto resetDataModel() -> void;
/**
* @brief onPaginatorFetching
* Sets current state to loading. Triggered when paginator's cache controller
* needs to fetch data from the remote endpoint.
*/
auto onPaginatorFetching() -> void;
signals:
auto stateChanged() -> void;
auto errorStringChanged() -> void;
@@ -238,6 +250,18 @@ signals:
auto totalPagesChanged() -> void;
private:
/**
* @brief boundaryCheck
* Helper function to check the requested index boundary
* @param index
* @return
*/
auto boundaryCheck(qsizetype index) const -> bool;
/**
* @brief m_dataRoles
* Data role map that connects ImageSearchModel::DataRole to it's QML accessor name
*/
static inline const QHash<int, QByteArray> m_dataRoles =
{
{
@@ -299,8 +323,6 @@ private:
FeaturedImagesPaginator *m_paginator = nullptr;
mutable QList<ImageCache> m_data;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
+52 -21
View File
@@ -36,10 +36,24 @@ FeaturedPacksModel::FeaturedPacksModel(QObject *parent) : QAbstractListModel{par
QObject::connect(
notifier,
&PaginationNotifier::dataChanged,
&PaginationNotifier::pageChanged,
this,
&FeaturedPacksModel::resetDataModel
);
QObject::connect(
notifier,
&PaginationNotifier::fetchComplete,
this,
&FeaturedPacksModel::resetState
);
QObject::connect(
notifier,
&PaginationNotifier::fetching,
this,
&FeaturedPacksModel::onPaginatorFetching
);
}
auto FeaturedPacksModel::rowCount(const QModelIndex &) const -> int
@@ -54,12 +68,12 @@ auto FeaturedPacksModel::rowCount(const QModelIndex &) const -> int
auto FeaturedPacksModel::data(const QModelIndex &index, int role) const -> QVariant
{
if(index.row() < 0 || m_paginator == nullptr)
if(index.row() < 0 || m_paginator == nullptr || index.row() >= m_paginator->count())
{
return {};
}
WallpaperCache dataPoint = m_data.at(index.row());
WallpaperCache dataPoint = m_paginator->at(index.row());
QVariant data;
@@ -107,10 +121,12 @@ auto FeaturedPacksModel::index(int row, int column, const QModelIndex &parent) c
{
Q_UNUSED(parent)
if(row < 0 || row >= m_data.count())
if(row < 0 || row >= m_paginator->count())
{
return {};
}
return createIndex(row, column, &m_data[row]);
return createIndex(row, column, &m_paginator[row]);
}
auto FeaturedPacksModel::columnCount(const QModelIndex &) const -> int
@@ -141,7 +157,7 @@ auto FeaturedPacksModel::setState(State state) -> void
}
m_state = state;
emit stateChanged();
Q_EMIT stateChanged();
}
auto FeaturedPacksModel::resetState() -> void
@@ -152,22 +168,19 @@ auto FeaturedPacksModel::resetState() -> void
auto FeaturedPacksModel::resetDataModel() -> void
{
//invalidate previous model data
beginRemoveRows(QModelIndex(), 0, m_data.count());
m_data.clear();
beginRemoveRows(QModelIndex(), 0, m_paginator->count());
endRemoveRows();
//signal new data
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
auto dataset = m_paginator->data();
for(const WallpaperCache& data : std::as_const(dataset))
{
m_data.append(data);
}
endInsertRows();
}
auto FeaturedPacksModel::onPaginatorFetching() -> void
{
setState(Loading);
}
auto FeaturedPacksModel::roleNames() const -> QHash<int, QByteArray>
{
return m_dataRoles;
@@ -186,7 +199,7 @@ auto FeaturedPacksModel::setErrorString(const QString &errorString) -> void
}
m_errorString = errorString;
emit errorStringChanged();
Q_EMIT errorStringChanged();
}
auto FeaturedPacksModel::resultsPerPage() const -> qsizetype
@@ -203,7 +216,13 @@ auto FeaturedPacksModel::setResultsPerPage(qsizetype resultsPerPage) -> void
{
if(m_paginator != nullptr)
{
m_paginator->setResultsPerPage(resultsPerPage);
QFuture<void> future = QtConcurrent::run
(
[this, resultsPerPage]
{
m_paginator->setResultsPerPage(resultsPerPage);
}
);
}
}
@@ -217,19 +236,31 @@ auto FeaturedPacksModel::totalResults() const -> qsizetype
return 0;
}
auto FeaturedPacksModel::nextPage() const -> void
auto FeaturedPacksModel::nextPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->next();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->next();
}
);
}
}
auto FeaturedPacksModel::previousPage() const -> void
auto FeaturedPacksModel::previousPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->previous();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->previous();
}
);
}
}
+9 -4
View File
@@ -35,6 +35,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QtConcurrent/QtConcurrentRun>
#include "paginators/featuredpackspaginator.h"
@@ -198,13 +199,13 @@ public:
* @brief nextPage
* Function for the QML frontend
*/
Q_INVOKABLE auto nextPage() const -> void;
Q_INVOKABLE auto nextPage() -> void;
/**
* @brief previousPage
* Function for the QML frontend
*/
Q_INVOKABLE auto previousPage() const -> void;
Q_INVOKABLE auto previousPage() -> void;
protected:
/**
@@ -226,6 +227,8 @@ protected:
auto resetDataModel() -> void;
auto onPaginatorFetching() -> void;
signals:
auto stateChanged() -> void;
auto errorStringChanged() -> void;
@@ -236,6 +239,10 @@ signals:
auto totalPagesChanged() -> void;
private:
/**
* @brief m_dataRoles
* Data role map that connects ImageSearchModel::DataRole to it's QML accessor name
*/
static inline const QHash<int, QByteArray> m_dataRoles =
{
{
@@ -289,8 +296,6 @@ private:
FeaturedPacksPaginator *m_paginator = nullptr;
mutable QList<WallpaperCache> m_data;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
+54 -24
View File
@@ -3,8 +3,6 @@
FeaturedVideosModel::FeaturedVideosModel(QObject *parent) : QAbstractListModel{parent}
{
setState(Loading);
m_paginator = new FeaturedVideosPaginator(this);
PaginationNotifier *notifier = static_cast<PaginationNotifier*>(m_paginator);
@@ -38,10 +36,24 @@ FeaturedVideosModel::FeaturedVideosModel(QObject *parent) : QAbstractListModel{p
QObject::connect(
notifier,
&PaginationNotifier::dataChanged,
&PaginationNotifier::pageChanged,
this,
&FeaturedVideosModel::resetDataModel
);
QObject::connect(
notifier,
&PaginationNotifier::fetchComplete,
this,
&FeaturedVideosModel::resetState
);
QObject::connect(
notifier,
&PaginationNotifier::fetching,
this,
&FeaturedVideosModel::onPaginatorFetching
);
}
auto FeaturedVideosModel::rowCount(const QModelIndex &) const -> int
@@ -56,12 +68,12 @@ auto FeaturedVideosModel::rowCount(const QModelIndex &) const -> int
auto FeaturedVideosModel::data(const QModelIndex &index, int role) const -> QVariant
{
if(index.row() < 0 || m_paginator == nullptr)
if(index.row() < 0 || m_paginator == nullptr || index.row() >= m_paginator->count())
{
return {};
}
VideoCache dataPoint = m_data.at(index.row());
VideoCache dataPoint = m_paginator->at(index.row());
QVariant data;
@@ -103,10 +115,17 @@ auto FeaturedVideosModel::index(int row, int column, const QModelIndex &parent)
{
Q_UNUSED(parent)
if(row < 0 || row >= m_data.count())
if(m_paginator == nullptr)
{
return {};
}
return createIndex(row, column, &m_data[row]);
if(row < 0 || row >= m_paginator->count())
{
return {};
}
return createIndex(row, column, &m_paginator[row]);
}
auto FeaturedVideosModel::columnCount(const QModelIndex &) const -> int
@@ -147,25 +166,18 @@ auto FeaturedVideosModel::resetState() -> void
auto FeaturedVideosModel::resetDataModel() -> void
{
setState(Loading);
//invalidate previous model data
beginRemoveRows(QModelIndex(), 0, m_data.count());
m_data.clear();
beginRemoveRows(QModelIndex(), 0, m_paginator->count());
endRemoveRows();
//signal new data
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
auto dataset = m_paginator->data();
for(const VideoCache& data : std::as_const(dataset))
{
m_data.append(data);
}
endInsertRows();
}
setState(Idle);
auto FeaturedVideosModel::onPaginatorFetching() -> void
{
setState(Loading);
}
auto FeaturedVideosModel::roleNames() const -> QHash<int, QByteArray>
@@ -203,7 +215,13 @@ auto FeaturedVideosModel::setResultsPerPage(qsizetype resultsPerPage) -> void
{
if(m_paginator != nullptr)
{
m_paginator->setResultsPerPage(resultsPerPage);
QFuture<void> future = QtConcurrent::run
(
[this, resultsPerPage]
{
m_paginator->setResultsPerPage(resultsPerPage);
}
);
}
}
@@ -217,19 +235,31 @@ auto FeaturedVideosModel::totalResults() const -> qsizetype
return 0;
}
auto FeaturedVideosModel::nextPage() const -> void
auto FeaturedVideosModel::nextPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->next();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->next();
}
);
}
}
auto FeaturedVideosModel::previousPage() const -> void
auto FeaturedVideosModel::previousPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->previous();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->previous();
}
);
}
}
+9 -4
View File
@@ -35,6 +35,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QtConcurrent/QtConcurrentRun>
#include "paginators/featuredvideospaginator.h"
@@ -196,13 +197,13 @@ public:
* @brief nextPage
* Function for the QML frontend
*/
Q_INVOKABLE auto nextPage() const -> void;
Q_INVOKABLE auto nextPage() -> void;
/**
* @brief previousPage
* Function for the QML frontend
*/
Q_INVOKABLE auto previousPage() const -> void;
Q_INVOKABLE auto previousPage() -> void;
protected:
/**
@@ -224,6 +225,8 @@ protected:
auto resetDataModel() -> void;
auto onPaginatorFetching() -> void;
signals:
auto stateChanged() -> void;
auto errorStringChanged() -> void;
@@ -234,6 +237,10 @@ signals:
auto totalPagesChanged() -> void;
private:
/**
* @brief m_dataRoles
* Data role map that connects ImageSearchModel::DataRole to it's QML accessor name
*/
static inline const QHash<int, QByteArray> m_dataRoles =
{
{
@@ -279,8 +286,6 @@ private:
FeaturedVideosPaginator *m_paginator = nullptr;
mutable QList<VideoCache> m_data;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
+117 -28
View File
@@ -2,8 +2,6 @@
ImageSearchModel::ImageSearchModel(QObject *parent) : QAbstractListModel{parent}
{
setState(Loading);
m_paginator = new ImageSearchPaginator(this);
PaginationNotifier *notifier = static_cast<PaginationNotifier*>(m_paginator);
@@ -37,11 +35,25 @@ ImageSearchModel::ImageSearchModel(QObject *parent) : QAbstractListModel{parent}
QObject::connect(
notifier,
&PaginationNotifier::dataChanged,
&PaginationNotifier::pageChanged,
this,
&ImageSearchModel::resetDataModel
);
QObject::connect(
notifier,
&PaginationNotifier::fetchComplete,
this,
&ImageSearchModel::resetState
);
QObject::connect(
notifier,
&PaginationNotifier::fetching,
this,
&ImageSearchModel::onPaginatorFetching
);
QObject::connect(
m_paginator,
&ImageSearchPaginator::queryChanged,
@@ -50,6 +62,14 @@ ImageSearchModel::ImageSearchModel(QObject *parent) : QAbstractListModel{parent}
);
}
ImageSearchModel::~ImageSearchModel()
{
if(m_paginator != nullptr)
{
delete m_paginator;
}
}
auto ImageSearchModel::rowCount(const QModelIndex &) const -> int
{
if(m_paginator != nullptr)
@@ -62,12 +82,12 @@ auto ImageSearchModel::rowCount(const QModelIndex &) const -> int
auto ImageSearchModel::data(const QModelIndex &index, int role) const -> QVariant
{
if(index.row() < 0 || m_paginator == nullptr)
if(index.row() < 0 || m_paginator == nullptr || index.row() >= m_paginator->count())
{
return {};
}
ImageCache dataPoint = m_data.at(index.row());
ImageCache dataPoint = m_paginator->at(index.row());
QVariant data;
@@ -112,6 +132,27 @@ auto ImageSearchModel::data(const QModelIndex &index, int role) const -> QVarian
case ExtraLargeUrlRole:
data = dataPoint.sources.value(QString::fromUtf8("large2x"));
break;
case PortraitSizeRole:
data = dataPoint.sourceSizes.value(QString::fromUtf8("portrait"));
break;
case LandscapeSizeRole:
data = dataPoint.sourceSizes.value(QString::fromUtf8("landscape"));
break;
case SmallSizeRole:
data = dataPoint.sourceSizes.value(QString::fromUtf8("small"));
break;
case OriginalSizeRole:
data = dataPoint.sourceSizes.value(QString::fromUtf8("original"));
break;
case MediumSizeRole:
data = dataPoint.sourceSizes.value(QString::fromUtf8("medium"));
break;
case LargeSizeRole:
data = dataPoint.sourceSizes.value(QString::fromUtf8("large"));
break;
case ExtraLargeSizeRole:
data = dataPoint.sourceSizes.value(QString::fromUtf8("large2x"));
break;
}
return data;
@@ -121,10 +162,12 @@ auto ImageSearchModel::index(int row, int column, const QModelIndex &parent) con
{
Q_UNUSED(parent)
if(row < 0 || row >= m_data.count())
if(row < 0 || row >= m_paginator->count())
{
return {};
}
return createIndex(row, column, &m_data[row]);
return createIndex(row, column, &m_paginator->at(row));
}
auto ImageSearchModel::columnCount(const QModelIndex &) const -> int
@@ -165,33 +208,26 @@ auto ImageSearchModel::resetState() -> void
auto ImageSearchModel::resetDataModel() -> void
{
setState(Loading);
//invalidate previous model data
beginRemoveRows(QModelIndex(), 0, m_data.count());
m_data.clear();
beginRemoveRows(QModelIndex(), 0, m_paginator->count());
endRemoveRows();
//signal new data
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
auto dataset = m_paginator->data();
for(const ImageCache& data : std::as_const(dataset))
{
m_data.append(data);
}
endInsertRows();
setState(Idle);
}
auto ImageSearchModel::hasNextPage() -> bool
auto ImageSearchModel::onPaginatorFetching() -> void
{
setState(Loading);
}
auto ImageSearchModel::hasNextPage() const -> bool
{
return m_paginator->page() < m_paginator->totalPages();
}
auto ImageSearchModel::hasPreviousPage() -> bool
auto ImageSearchModel::hasPreviousPage() const -> bool
{
return m_paginator->page() > 0;
}
@@ -231,7 +267,42 @@ auto ImageSearchModel::setResultsPerPage(qsizetype resultsPerPage) -> void
{
if(m_paginator != nullptr)
{
m_paginator->setResultsPerPage(resultsPerPage);
QFuture<void> future = QtConcurrent::run
(
[this, resultsPerPage]
{
qsizetype difference = resultsPerPage - m_paginator->resultsPerPage();
m_paginator->setResultsPerPage(resultsPerPage);
if(difference > 0)
{
qsizetype firstIndex = m_paginator->resultsPerPage() - difference;
beginInsertRows
(
QModelIndex(),
firstIndex,
m_paginator->count() - 1
);
endInsertRows();
}
else if(difference < 0)
{
qsizetype firstIndex = m_paginator->resultsPerPage();
beginRemoveRows
(
QModelIndex(),
firstIndex,
firstIndex - difference
);
endRemoveRows();
}
}
);
}
}
@@ -245,19 +316,31 @@ auto ImageSearchModel::totalResults() const -> qsizetype
return 0;
}
auto ImageSearchModel::nextPage() const -> void
auto ImageSearchModel::nextPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->next();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->next();
}
);
}
}
auto ImageSearchModel::previousPage() const -> void
auto ImageSearchModel::previousPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->previous();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->previous();
}
);
}
}
@@ -275,7 +358,13 @@ auto ImageSearchModel::setQuery(const QString &query) -> void
{
if(m_paginator != nullptr)
{
m_paginator->setQuery(query);
QFuture<void> future = QtConcurrent::run
(
[this, query]
{
m_paginator->setQuery(query);
}
);
}
}
+104 -9
View File
@@ -35,6 +35,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QtConcurrent/QtConcurrentRun>
#include "paginators/imagesearchpaginator.h"
@@ -74,11 +75,19 @@ public:
OriginalUrlRole,
MediumUrlRole,
LargeUrlRole,
ExtraLargeUrlRole
ExtraLargeUrlRole,
PortraitSizeRole,
LandscapeSizeRole,
SmallSizeRole,
OriginalSizeRole,
MediumSizeRole,
LargeSizeRole,
ExtraLargeSizeRole
};
Q_ENUM(DataRole)
explicit ImageSearchModel(QObject *parent = nullptr);
~ImageSearchModel();
auto rowCount(const QModelIndex &parent = QModelIndex()) const -> int override;
@@ -200,13 +209,13 @@ public:
* @brief nextPage
* Function for the QML frontend
*/
Q_INVOKABLE auto nextPage() const -> void;
Q_INVOKABLE auto nextPage() -> void;
/**
* @brief previousPage
* Function for the QML frontend
*/
Q_INVOKABLE auto previousPage() const -> void;
Q_INVOKABLE auto previousPage() -> void;
/**
* @brief query
@@ -217,20 +226,36 @@ public:
/**
* @brief setQuery
* Sets the current
* Sets the current search query
* @param query
*/
auto setQuery(const QString &query) -> void;
/**
* @brief hasNextPage
* Calculates if there are more pages based on the current page calculation
* @return
*/
auto hasNextPage() const -> bool;
/**
* @brief hasPreviousPage
* If page is greater than 1, returns true
* @return
*/
auto hasPreviousPage() const -> bool;
protected:
/**
* @brief setErrorString
* Sets the error string to be displayed to the user
* @param errorString
*/
auto setErrorString(const QString &errorString) -> void;
/**
* @brief setState
* Sets the current state for QML
* @param state
*/
auto setState(State state) -> void;
@@ -240,23 +265,67 @@ protected:
*/
auto resetState() -> void;
/**
* @brief resetDataModel
* Clears the data model and creates a new one if data exists
*/
auto resetDataModel() -> void;
auto hasNextPage() -> bool;
auto hasPreviousPage() -> bool;
/**
* @brief onPaginatorFetching
* Sets current state to loading. Triggered when paginator's cache controller
* needs to fetch data from the remote endpoint.
*/
auto onPaginatorFetching() -> void;
signals:
/**
* @brief stateChanged
* Signaled when reported state changes
*/
auto stateChanged() -> void;
/**
* @brief errorStringChanged
* Signaled when reported error message changes
*/
auto errorStringChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto resultsPerPageChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto pageChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto totalResultsChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto totalPagesChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto queryChanged() -> void;
private:
/**
* @brief m_dataRoles
* Data role map that connects ImageSearchModel::DataRole to it's QML accessor name
*/
static inline const QHash<int, QByteArray> m_dataRoles =
{
{
@@ -310,6 +379,34 @@ private:
{
static_cast<int>(ExtraLargeUrlRole),
QByteArray("extraLarge")
},
{
static_cast<int>(PortraitSizeRole),
QByteArray("portraitSize")
},
{
static_cast<int>(LandscapeSizeRole),
QByteArray("landscapeSize")
},
{
static_cast<int>(SmallSizeRole),
QByteArray("smallSize")
},
{
static_cast<int>(OriginalSizeRole),
QByteArray("originalSize")
},
{
static_cast<int>(MediumSizeRole),
QByteArray("mediumSize")
},
{
static_cast<int>(LargeSizeRole),
QByteArray("largeSize")
},
{
static_cast<int>(ExtraLargeSizeRole),
QByteArray("extraLargeSize")
}
};
State m_state = Idle;
@@ -318,8 +415,6 @@ private:
ImageSearchPaginator *m_paginator = nullptr;
mutable QList<ImageCache> m_data;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
+39 -18
View File
@@ -38,10 +38,24 @@ NewestPacksModel::NewestPacksModel(QObject *parent) : QAbstractListModel{parent}
QObject::connect(
notifier,
&PaginationNotifier::dataChanged,
&PaginationNotifier::pageChanged,
this,
&NewestPacksModel::resetDataModel
);
QObject::connect(
notifier,
&PaginationNotifier::fetchComplete,
this,
&NewestPacksModel::resetState
);
QObject::connect(
notifier,
&PaginationNotifier::fetching,
this,
&NewestPacksModel::onPaginatorFetching
);
}
auto NewestPacksModel::rowCount(const QModelIndex &) const -> int
@@ -56,12 +70,12 @@ auto NewestPacksModel::rowCount(const QModelIndex &) const -> int
auto NewestPacksModel::data(const QModelIndex &index, int role) const -> QVariant
{
if(index.row() < 0 || m_paginator == nullptr)
if(index.row() < 0 || m_paginator == nullptr || index.row() >= m_paginator->count())
{
return {};
}
WallpaperCache dataPoint = m_data.at(index.row());
WallpaperCache dataPoint = m_paginator->at(index.row());
QVariant data;
@@ -109,10 +123,10 @@ auto NewestPacksModel::index(int row, int column, const QModelIndex &parent) con
{
Q_UNUSED(parent)
if(row < 0 || row >= m_data.count())
if(row < 0 || row >= m_paginator->count())
return {};
return createIndex(row, column, &m_data[row]);
return createIndex(row, column, &m_paginator[row]);
}
auto NewestPacksModel::columnCount(const QModelIndex &) const -> int
@@ -153,23 +167,18 @@ auto NewestPacksModel::resetState() -> void
auto NewestPacksModel::resetDataModel() -> void
{
setState(Loading);
//invalidate previous model data
beginRemoveRows(QModelIndex(), 0, m_data.count());
m_data.clear();
beginRemoveRows(QModelIndex(), 0, m_paginator->count());
endRemoveRows();
//signal new data
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
auto dataset = m_paginator->data();
for(const WallpaperCache& data : std::as_const(dataset))
{
m_data.append(data);
}
endInsertRows();
setState(Idle);
}
auto NewestPacksModel::onPaginatorFetching() -> void
{
setState(Loading);
}
auto NewestPacksModel::roleNames() const -> QHash<int, QByteArray>
@@ -225,7 +234,13 @@ auto NewestPacksModel::nextPage() const -> void
{
if(m_paginator != nullptr)
{
m_paginator->next();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->next();
}
);
}
}
@@ -233,7 +248,13 @@ auto NewestPacksModel::previousPage() const -> void
{
if(m_paginator != nullptr)
{
m_paginator->previous();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->previous();
}
);
}
}
+3 -2
View File
@@ -35,6 +35,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QtConcurrent/QtConcurrentRun>
#include "paginators/newestpackspaginator.h"
@@ -236,6 +237,8 @@ protected:
*/
auto resetDataModel() -> void;
auto onPaginatorFetching() -> void;
signals:
auto stateChanged() -> void;
auto errorStringChanged() -> void;
@@ -299,8 +302,6 @@ private:
NewestPacksPaginator *m_paginator = nullptr;
mutable QList<WallpaperCache> m_data;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
+328
View File
@@ -0,0 +1,328 @@
#include "packsearchmodel.h"
PackSearchModel::PackSearchModel(QObject *parent) : QAbstractListModel{parent}
{
m_paginator = new PackSearchPaginator(this);
PaginationNotifier *notifier = static_cast<PaginationNotifier*>(m_paginator);
QObject::connect(
notifier,
&PaginationNotifier::resultsPerPageChanged,
this,
&PackSearchModel::resultsPerPageChanged
);
QObject::connect(
notifier,
&PaginationNotifier::totalResultsChanged,
this,
&PackSearchModel::totalResultsChanged
);
QObject::connect(
notifier,
&PaginationNotifier::totalPagesChanged,
this,
&PackSearchModel::totalPagesChanged
);
QObject::connect(
notifier,
&PaginationNotifier::pageChanged,
this,
&PackSearchModel::pageChanged
);
QObject::connect(
notifier,
&PaginationNotifier::pageChanged,
this,
&PackSearchModel::resetDataModel
);
QObject::connect(
notifier,
&PaginationNotifier::fetchComplete,
this,
&PackSearchModel::resetState
);
QObject::connect(
notifier,
&PaginationNotifier::fetching,
this,
&PackSearchModel::onPaginatorFetching
);
QObject::connect(
m_paginator,
&PaginationNotifier::queryChanged,
this,
&PackSearchModel::queryChanged
);
}
auto PackSearchModel::rowCount(const QModelIndex &) const -> int
{
if(m_paginator != nullptr)
{
return m_paginator->count();
}
return 0;
}
auto PackSearchModel::data(const QModelIndex &index, int role) const -> QVariant
{
if(m_paginator == nullptr)
{
return {};
}
if(index.row() < 0 || index.row() >= m_paginator->count() )
{
return {};
}
WallpaperCache dataPoint = m_paginator->at(index.row());
QVariant data;
switch(static_cast<DataRole>(role))
{
case UuidRole:
data = dataPoint.uuid;
break;
case AuthorRole:
data = dataPoint.author;
break;
case DescriptionRole:
data = dataPoint.description;
break;
case NameRole:
data = dataPoint.name;
break;
case ThumbnailRole:
data = dataPoint.thumbnail;
break;
case CreatedDateRole:
data = dataPoint.createdDate;
break;
case AuthorIdRole:
data = dataPoint.authorId;
break;
case PriceRole:
data = dataPoint.price;
break;
case CurrencyRole:
data = dataPoint.currency;
break;
case DownloadCountRole:
data = dataPoint.downloadCount;
break;
case TypeRole:
data = dataPoint.type;
break;
}
return data;
}
auto PackSearchModel::index(int row, int column, const QModelIndex &parent) const -> QModelIndex
{
Q_UNUSED(parent)
if(row < 0 || row >= m_paginator->count())
return {};
return createIndex(row, column, &m_paginator[row]);
}
auto PackSearchModel::columnCount(const QModelIndex &) const -> int
{
return 0;
}
auto PackSearchModel::parent(const QModelIndex &) const -> QModelIndex
{
return {};
}
auto PackSearchModel::setData(const QModelIndex &, const QVariant &, int) -> bool
{
return false;
}
auto PackSearchModel::state() const -> PackSearchModel::State
{
return m_state;
}
auto PackSearchModel::setState(State state) -> void
{
if (m_state == state)
{
return;
}
m_state = state;
emit stateChanged();
}
auto PackSearchModel::resetState() -> void
{
setState(Idle);
}
auto PackSearchModel::resetDataModel() -> void
{
//invalidate previous model data
beginRemoveRows(QModelIndex(), 0, m_paginator->count());
endRemoveRows();
//signal new data
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
endInsertRows();
}
auto PackSearchModel::hasNextPage() const -> bool
{
return m_paginator->page() < m_paginator->totalPages();
}
auto PackSearchModel::hasPreviousPage() const -> bool
{
return m_paginator->page() > 0;
}
auto PackSearchModel::onPaginatorFetching() -> void
{
setState(Loading);
}
auto PackSearchModel::roleNames() const -> QHash<int, QByteArray>
{
return m_dataRoles;
}
auto PackSearchModel::errorString() const -> QString
{
return m_errorString;
}
auto PackSearchModel::setErrorString(const QString &errorString) -> void
{
if (m_errorString == errorString)
{
return;
}
m_errorString = errorString;
emit errorStringChanged();
}
auto PackSearchModel::resultsPerPage() const -> qsizetype
{
if(m_paginator != nullptr)
{
return m_paginator->resultsPerPage();
}
return 0;
}
auto PackSearchModel::setResultsPerPage(qsizetype resultsPerPage) -> void
{
if(m_paginator != nullptr)
{
QFuture<void> future = QtConcurrent::run
(
[this, resultsPerPage]
{
m_paginator->setResultsPerPage(resultsPerPage);
}
);
}
}
auto PackSearchModel::totalResults() const -> qsizetype
{
if(m_paginator != nullptr)
{
return m_paginator->totalResults();
}
return 0;
}
auto PackSearchModel::nextPage() -> void
{
if(m_paginator != nullptr)
{
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->next();
}
);
}
}
auto PackSearchModel::previousPage() -> void
{
if(m_paginator != nullptr)
{
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->previous();
}
);
}
}
auto PackSearchModel::query() const -> QString
{
if(m_paginator != nullptr)
{
return m_paginator->query();
}
return {};
}
auto PackSearchModel::setQuery(const QString &query) -> void
{
if(m_paginator != nullptr)
{
QFuture<void> future = QtConcurrent::run
(
[this, query]
{
m_paginator->setQuery(query);
}
);
}
}
qsizetype PackSearchModel::page() const
{
if(m_paginator != nullptr)
{
return m_paginator->page();
}
return 0;
}
auto PackSearchModel::totalPages() const -> qsizetype
{
if(m_paginator != nullptr)
{
return m_paginator->totalPages();
}
return 0;
}
+383
View File
@@ -0,0 +1,383 @@
/*
* 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 <https://www.gnu.org/licenses/>
*/
#ifndef PackSearchModel_H
#define PackSearchModel_H
#include <QObject>
#include <QQmlEngine>
#include <QList>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QAbstractListModel>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QtConcurrent/QtConcurrentRun>
#include "paginators/packsearchpaginator.h"
/**
* @brief The PackSearchModel class
*/
class KOMPLEX_EXPORT PackSearchModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
public:
/**
* @brief Used to control the UI state
*/
enum State
{
Idle,
Loading,
Error
};
Q_ENUM(State)
/**
* @brief Data roles used by the view to request data
*/
enum DataRole {
UuidRole = Qt::UserRole + 1,
AuthorRole,
AuthorIdRole,
DescriptionRole,
NameRole,
ThumbnailRole,
CreatedDateRole,
PriceRole,
CurrencyRole,
DownloadCountRole,
TypeRole
};
Q_ENUM(DataRole)
explicit PackSearchModel(QObject *parent = nullptr);
auto rowCount(const QModelIndex &parent = QModelIndex()) const -> int override;
/**
* @brief data
* Used by the view to pull data from the model.
* Required by QAbstractListModel
* @param index
* @param role
* @return
*/
auto data(
const QModelIndex &index,
int role = Qt::DisplayRole
) const -> QVariant override;
/**
* @brief index
* Used by the view to create an index for the data point.
* Required by QAbstractListModel
* @param row
* @param column
* @param parent
* @return
*/
auto index(
int row,
int column,
const QModelIndex &parent = QModelIndex()
) const -> QModelIndex override;
/**
* @brief columnCount
* Required by QAbstractListModel, but just returns 0
* since we dont use cols
* @param parent
* @return
*/
auto columnCount(
const QModelIndex &parent = QModelIndex()
) const -> int override;
/**
* @brief parent
* @param index
* @return
*/
auto parent(const QModelIndex &index) const -> QModelIndex override;
/**
* @brief setData
* Required by QAbstractListModel but not used
* @param index
* @param value
* @param role
* @return true always
*/
auto setData(
const QModelIndex &index,
const QVariant &value,
int role = Qt::EditRole
) -> bool override;
/**
* @brief state
* Current Model State
* @return
*/
auto state() const -> State;
/**
* @brief roleNames
* Used to tell the view what data roles are available
* and their QML friendly names
* @return
*/
auto roleNames() const -> QHash<int, QByteArray> override;
/**
* @brief errorString
* User-friendly error string to be reported to the user
* @return
*/
auto errorString() const -> QString;
/**
* @brief resultsPerPage
* @return
*/
auto resultsPerPage() const -> qsizetype;
/**
* @brief setResultsPerPage
* @param resultsPerPage
*/
auto setResultsPerPage(qsizetype resultsPerPage) -> void;
/**
* @brief totalResults
* Total number of results available
* @return
*/
auto totalResults() const -> qsizetype;
/**
* @brief page
* @return
*/
auto page() const -> qsizetype;
/**
* @brief totalPages
* Total pages based on the current resultsPerPage
* @return
*/
auto totalPages() const -> qsizetype;
/**
* @brief nextPage
* Function for the QML frontend
*/
Q_INVOKABLE auto nextPage() -> void;
/**
* @brief previousPage
* Function for the QML frontend
*/
Q_INVOKABLE auto previousPage() -> void;
/**
* @brief query
* Current search query
* @return
*/
auto query() const -> QString;
/**
* @brief setQuery
* Sets the current search query
* @param query
*/
auto setQuery(const QString &query) -> void;
/**
* @brief hasNextPage
* Calculates if there are more pages based on the current page calculation
* @return
*/
auto hasNextPage() const -> bool;
/**
* @brief hasPreviousPage
* If page is greater than 1, returns true
* @return
*/
auto hasPreviousPage() const -> bool;
protected:
/**
* @brief onPaginatorFetching
* Sets current state to loading. Triggered when paginator's cache controller
* needs to fetch data from the remote endpoint.
*/
auto onPaginatorFetching() -> void;
/**
* @brief setErrorString
* @param errorString
*/
auto setErrorString(const QString &errorString) -> void;
/**
* @brief setState
* @param state
*/
auto setState(State state) -> void;
/**
* @brief resetState
*/
auto resetState() -> void;
/**
* @brief resetDataModel
* Clears the data model and creates a new one if data exists
*/
auto resetDataModel() -> void;
signals:
/**
* @brief stateChanged
* Signaled when reported state changes
*/
auto stateChanged() -> void;
/**
* @brief errorStringChanged
* Signaled when reported error message changes
*/
auto errorStringChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto resultsPerPageChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto pageChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto totalResultsChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto totalPagesChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto queryChanged() -> void;
private:
/**
* Data role map that connects PackSearchModel::DataRole to it's QML accessor name
*/
static inline const QHash<int, QByteArray> m_dataRoles =
{
{
static_cast<int>(UuidRole),
QByteArray("uuid")
},
{
static_cast<int>(AuthorRole),
QByteArray("author")
},
{
static_cast<int>(AuthorIdRole),
QByteArray("authorId")
},
{
static_cast<int>(DescriptionRole),
QByteArray("description")
},
{
static_cast<int>(NameRole),
QByteArray("name")
},
{
static_cast<int>(ThumbnailRole),
QByteArray("thumbnail")
},
{
static_cast<int>(CreatedDateRole),
QByteArray("createdDate")
},
{
static_cast<int>(PriceRole),
QByteArray("price")
},
{
static_cast<int>(CurrencyRole),
QByteArray("currency")
},
{
static_cast<int>(DownloadCountRole),
QByteArray("downloadCount")
},
{
static_cast<int>(TypeRole),
QByteArray("type")
}
};
State m_state = Idle;
QString m_errorString = QString();
PackSearchPaginator *m_paginator = nullptr;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
Q_PROPERTY(qsizetype totalResults READ totalResults NOTIFY totalResultsChanged FINAL)
Q_PROPERTY(qsizetype totalPages READ totalPages NOTIFY totalPagesChanged FINAL)
Q_PROPERTY(qsizetype page READ page NOTIFY pageChanged FINAL)
Q_PROPERTY(bool hasNextPage READ hasNextPage NOTIFY pageChanged FINAL)
Q_PROPERTY(bool hasPreviousPage READ hasPreviousPage NOTIFY pageChanged FINAL)
Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged FINAL)
};
Q_DECLARE_METATYPE(PackSearchModel)
#endif // PackSearchModel_H
@@ -39,6 +39,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_CUBEMAPS_FEATURED)
);
controller()->setWindowSize(20);
}
};
@@ -39,6 +39,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_IMAGES_FEATURED)
);
controller()->setWindowSize(20);
}
};
@@ -39,6 +39,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_PACKS_FEATURED)
);
controller()->setWindowSize(20);
}
};
@@ -39,6 +39,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_SHADERS_FEATURED)
);
controller()->setWindowSize(20);
}
};
@@ -39,6 +39,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_VIDEOS_FEATURED)
);
controller()->setWindowSize(20);
}
};
+29 -39
View File
@@ -62,46 +62,12 @@ public:
return m_uri;
}
/**
* @brief query
* Current query string, if the API Endpoint supports queries
* @return
*/
[[nodiscard]]
auto query() const -> QString
{
return m_query;
}
/**
* @brief setQuery
* Sets the new query string and updates the controller
* @param query
*/
auto setQuery(const QString &query) -> void
{
if(query == m_query)
{
return;
}
m_query = query;
Q_EMIT queryChanged();
reset();
}
signals:
/**
* @brief uriChanged
*/
auto uriChanged() -> void;
/**
* @brief queryChanged
*/
auto queryChanged() -> void;
protected:
/**
* @brief fetch
@@ -124,6 +90,11 @@ protected:
{}
);
if(queryable() && query().isEmpty())
{
return {};
}
QUrl url(
QString::fromUtf8("%1/%2/%3").arg(
KOMPLEX_API_HOST,
@@ -138,7 +109,7 @@ protected:
{
{
QByteArray("query"),
m_query.toUtf8()
query().toUtf8()
},
{
QByteArray("offset"),
@@ -181,13 +152,28 @@ protected:
const QVariantMap sources = resultObject.value(QString::fromUtf8("src")).toObject().toVariantMap();
QMap<QString, QString> sourceMap;
QMap<QString, QString> sizeMap;
QMapIterator<QString,QVariant> sourceIterator(sources);
while(sourceIterator.hasNext())
{
sourceIterator.next();
sourceMap.insert(sourceIterator.key(), sourceIterator.value().toString());
QString url = sourceIterator.value().toString();
sourceMap.insert(sourceIterator.key(), url);
auto matches = s_sizeExpression.match(url);
if(matches.hasMatch())
{
QString size = matches.captured();
size.remove(QString::fromUtf8("w="));
size.remove(QString::fromUtf8("h="));
size.replace(QLatin1Char('&'), QLatin1Char('x'));
sizeMap.insert(sourceIterator.key(), std::move(size));
}
}
fetchedResults.append(
@@ -202,6 +188,7 @@ protected:
resultObject.value(QString::fromUtf8("photographer_url")).toString(),
resultObject.value(QString::fromUtf8("photographer_id")).toInteger(),
std::move(sourceMap),
std::move(sizeMap),
resultObject.value(QString::fromUtf8("url")).toString()
}
);
@@ -307,10 +294,13 @@ private:
QString m_uri;
/**
* @brief m_query
* The query string used in fetch operations
* @brief s_sizeExpression
* Regular expression used to extract size data from the url
*/
QString m_query;
inline static QRegularExpression s_sizeExpression = QRegularExpression
(
QString::fromUtf8("[h]\\=\\d{1,}\\&[w]\\=\\d{1,}")
);
};
#endif // IMAGEPAGINATOR_H
@@ -41,6 +41,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_IMAGES_SEARCH)
);
setQueryable(true);
}
};
@@ -39,6 +39,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_PACKS_NEWEST)
);
controller()->setWindowSize(20);
}
};
@@ -16,8 +16,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>
*/
#ifndef SEARCHPACKSPAGINATOR_H
#define SEARCHPACKSPAGINATOR_H
#ifndef PACKSEARCHPAGINATOR_H
#define PACKSEARCHPAGINATOR_H
#include <QObject>
#include <QNetworkAccessManager>
@@ -31,15 +31,17 @@
#include "wallpaperpaginator.h"
class KOMPLEX_EXPORT SearchPacksPaginator : public WallpaperPaginator
class KOMPLEX_EXPORT PackSearchPaginator : public WallpaperPaginator
{
public:
explicit SearchPacksPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
explicit PackSearchPaginator(QObject *parent = nullptr) : WallpaperPaginator(parent)
{
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_PACKS_SEARCH)
);
setQueryable(true);
}
};
#endif // SEARCHPACKSPAGINATOR_H
#endif // PACKSEARCHPAGINATOR_H
+12 -37
View File
@@ -62,47 +62,12 @@ public:
return m_uri;
}
/**
* @brief query
* Current query string, if the API Endpoint supports queries
* @return
*/
[[nodiscard]]
auto query() const -> QString
{
return m_query;
}
/**
* @brief setQuery
* Sets the new query string and updates the controller
* @param query
*/
auto setQuery(const QString &query) -> void
{
if(query == m_query)
{
return;
}
m_query = query;
Q_EMIT queryChanged();
reset();
setOffset(0);
}
signals:
/**
* @brief uriChanged
*/
auto uriChanged() -> void;
/**
* @brief queryChanged
*/
auto queryChanged() -> void;
protected:
/**
* @brief fetch
@@ -125,6 +90,11 @@ protected:
{}
);
if(queryable() && query().isEmpty())
{
return {};
}
QUrl url(
QString::fromUtf8("%1/%2/%3").arg(
KOMPLEX_API_HOST,
@@ -139,7 +109,7 @@ protected:
{
{
QByteArray("query"),
m_query.toUtf8()
query().toUtf8()
},
{
QByteArray("offset"),
@@ -258,7 +228,12 @@ protected:
QNetworkReply *reply = manager->post(request, nullptr);
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
QObject::connect(reply, &QNetworkReply::errorOccurred, &loop,
QObject::connect
(
reply,
&QNetworkReply::errorOccurred,
&loop,
[&loop](QNetworkReply::NetworkError error) -> void
{
loop.quit();
@@ -39,6 +39,8 @@ public:
setUri(
QString::fromUtf8(KOMPLEX_ENDPOINT_VIDEOS_SEARCH)
);
setQueryable(true);
}
};
@@ -98,11 +98,6 @@ signals:
*/
auto uriChanged() -> void;
/**
* @brief queryChanged
*/
auto queryChanged() -> void;
protected:
/**
* @brief fetch
@@ -125,6 +120,11 @@ protected:
{}
);
if(queryable() && query().isEmpty())
{
return {};
}
QUrl url(
QString::fromUtf8("%1/%2/%3").arg(
KOMPLEX_API_HOST,
+62 -28
View File
@@ -2,8 +2,6 @@
VideoSearchModel::VideoSearchModel(QObject *parent) : QAbstractListModel{parent}
{
setState(Loading);
m_paginator = new VideoSearchPaginator(this);
PaginationNotifier *notifier = static_cast<PaginationNotifier*>(m_paginator);
@@ -37,11 +35,25 @@ VideoSearchModel::VideoSearchModel(QObject *parent) : QAbstractListModel{parent}
QObject::connect(
notifier,
&PaginationNotifier::dataChanged,
&PaginationNotifier::pageChanged,
this,
&VideoSearchModel::resetDataModel
);
QObject::connect(
notifier,
&PaginationNotifier::fetchComplete,
this,
&VideoSearchModel::resetState
);
QObject::connect(
notifier,
&PaginationNotifier::fetching,
this,
&VideoSearchModel::onPaginatorFetching
);
QObject::connect(
m_paginator,
&VideoSearchPaginator::queryChanged,
@@ -62,12 +74,17 @@ auto VideoSearchModel::rowCount(const QModelIndex &) const -> int
auto VideoSearchModel::data(const QModelIndex &index, int role) const -> QVariant
{
if(index.row() < 0 || m_paginator == nullptr)
if(m_paginator == nullptr)
{
return {};
}
VideoCache dataPoint = m_data.at(index.row());
if(index.row() < 0 || index.row() >= m_paginator->count() )
{
return {};
}
VideoCache dataPoint = m_paginator->at(index.row());
QVariant data;
@@ -109,10 +126,10 @@ auto VideoSearchModel::index(int row, int column, const QModelIndex &parent) con
{
Q_UNUSED(parent)
if(row < 0 || row >= m_data.count())
if(row < 0 || row >= m_paginator->count())
return {};
return createIndex(row, column, &m_data[row]);
return createIndex(row, column, &m_paginator[row]);
}
auto VideoSearchModel::columnCount(const QModelIndex &) const -> int
@@ -153,37 +170,30 @@ auto VideoSearchModel::resetState() -> void
auto VideoSearchModel::resetDataModel() -> void
{
setState(Loading);
//invalidate previous model data
beginRemoveRows(QModelIndex(), 0, m_data.count());
m_data.clear();
beginRemoveRows(QModelIndex(), 0, m_paginator->count());
endRemoveRows();
//signal new data
beginInsertRows(QModelIndex(), 0, m_paginator->count() - 1);
auto dataset = m_paginator->data();
for(const VideoCache& data : std::as_const(dataset))
{
m_data.append(data);
}
endInsertRows();
setState(Idle);
}
auto VideoSearchModel::hasNextPage() -> bool
auto VideoSearchModel::hasNextPage() const -> bool
{
return m_paginator->page() < m_paginator->totalPages();
}
auto VideoSearchModel::hasPreviousPage() -> bool
auto VideoSearchModel::hasPreviousPage() const -> bool
{
return m_paginator->page() > 0;
}
auto VideoSearchModel::onPaginatorFetching() -> void
{
setState(Loading);
}
auto VideoSearchModel::roleNames() const -> QHash<int, QByteArray>
{
return m_dataRoles;
@@ -219,7 +229,13 @@ auto VideoSearchModel::setResultsPerPage(qsizetype resultsPerPage) -> void
{
if(m_paginator != nullptr)
{
m_paginator->setResultsPerPage(resultsPerPage);
QFuture<void> future = QtConcurrent::run
(
[this, resultsPerPage]
{
m_paginator->setResultsPerPage(resultsPerPage);
}
);
}
}
@@ -233,19 +249,31 @@ auto VideoSearchModel::totalResults() const -> qsizetype
return 0;
}
auto VideoSearchModel::nextPage() const -> void
auto VideoSearchModel::nextPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->next();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->next();
}
);
}
}
auto VideoSearchModel::previousPage() const -> void
auto VideoSearchModel::previousPage() -> void
{
if(m_paginator != nullptr)
{
m_paginator->previous();
QFuture<void> future = QtConcurrent::run
(
[this]
{
m_paginator->previous();
}
);
}
}
@@ -263,7 +291,13 @@ auto VideoSearchModel::setQuery(const QString &query) -> void
{
if(m_paginator != nullptr)
{
m_paginator->setQuery(query);
QFuture<void> future = QtConcurrent::run
(
[this, query]
{
m_paginator->setQuery(query);
}
);
}
}
+66 -9
View File
@@ -35,6 +35,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QtConcurrent/QtConcurrentRun>
#include "paginators/videosearchpaginator.h"
@@ -196,13 +197,13 @@ public:
* @brief nextPage
* Function for the QML frontend
*/
Q_INVOKABLE auto nextPage() const -> void;
Q_INVOKABLE auto nextPage() -> void;
/**
* @brief previousPage
* Function for the QML frontend
*/
Q_INVOKABLE auto previousPage() const -> void;
Q_INVOKABLE auto previousPage() -> void;
/**
* @brief query
@@ -213,12 +214,34 @@ public:
/**
* @brief setQuery
* Sets the current
* Sets the current search query
* @param query
*/
auto setQuery(const QString &query) -> void;
/**
* @brief hasNextPage
* Calculates if there are more pages based on the current page calculation
* @return
*/
auto hasNextPage() const -> bool;
/**
* @brief hasPreviousPage
* If page is greater than 1, returns true
* @return
*/
auto hasPreviousPage() const -> bool;
protected:
/**
* @brief onPaginatorFetching
* Sets current state to loading. Triggered when paginator's cache controller
* needs to fetch data from the remote endpoint.
*/
auto onPaginatorFetching() -> void;
/**
* @brief setErrorString
* @param errorString
@@ -236,23 +259,59 @@ protected:
*/
auto resetState() -> void;
/**
* @brief resetDataModel
* Clears the data model and creates a new one if data exists
*/
auto resetDataModel() -> void;
auto hasNextPage() -> bool;
auto hasPreviousPage() -> bool;
signals:
/**
* @brief stateChanged
* Signaled when reported state changes
*/
auto stateChanged() -> void;
/**
* @brief errorStringChanged
* Signaled when reported error message changes
*/
auto errorStringChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto resultsPerPageChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto pageChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto totalResultsChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto totalPagesChanged() -> void;
/**
* @brief pageChanged
* Forwarding signal connected in constructor
*/
auto queryChanged() -> void;
private:
/**
* Data role map that connects VideoSearchModel::DataRole to it's QML accessor name
*/
static inline const QHash<int, QByteArray> m_dataRoles =
{
{
@@ -298,8 +357,6 @@ private:
VideoSearchPaginator *m_paginator = nullptr;
mutable QList<VideoCache> m_data;
Q_PROPERTY(State state READ state WRITE setState RESET resetState NOTIFY stateChanged FINAL)
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged FINAL)
Q_PROPERTY(qsizetype resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)