♻️ refactor

This commit is contained in:
2026-02-28 02:12:20 +01:00
parent 922ecac3b0
commit f03bdf4e6a
25 changed files with 876 additions and 806 deletions
+3 -1
View File
@@ -3,9 +3,11 @@ qt_add_qml_module(${CORELIB_NAME}
URI ${COREMODULE_URI} URI ${COREMODULE_URI}
VERSION ${MODULE_VERSION_MAJOR}.${MODULE_VERSION_MINOR} VERSION ${MODULE_VERSION_MAJOR}.${MODULE_VERSION_MINOR}
SOURCES SOURCES
Provider/carousel.hpp Provider/bootstrap.hpp
Cache/manager.hpp Cache/manager.cpp Cache/manager.hpp Cache/manager.cpp
Image/data.hpp Image/data.cpp Image/data.hpp Image/data.cpp
Image/model.hpp Image/model.cpp Image/model.hpp Image/model.cpp Image/proxymodel.cpp
Image/manager.hpp Image/manager.cpp
Palette/data.hpp Palette/data.hpp
Palette/manager.hpp Palette/manager.cpp Palette/manager.hpp Palette/manager.cpp
Palette/domcolor.hpp Palette/domcolor.cpp Palette/domcolor.hpp Palette/domcolor.cpp
-6
View File
@@ -38,12 +38,6 @@ Manager::~Manager() {
} }
WR_DEBUG(u"Closing %1 cache db connection(s)"_s.arg(names.size())); WR_DEBUG(u"Closing %1 cache db connection(s)"_s.arg(names.size()));
for (const QString& connName : std::as_const(names)) { for (const QString& connName : std::as_const(names)) {
{
// Scope: release the QSqlDatabase copy before removeDatabase()
QSqlDatabase db = QSqlDatabase::database(connName, false);
if (db.isOpen())
db.close();
}
QSqlDatabase::removeDatabase(connName); QSqlDatabase::removeDatabase(connName);
} }
} }
+31 -5
View File
@@ -46,8 +46,8 @@
// style.window_width number 750 Initial window width // style.window_width number 750 Initial window width
// style.window_height number 500 Initial window height // style.window_height number 500 Initial window height
// //
// sort.type string "name" Sorting type: "none", "name", "date", "size" // sort.type string "date" Sorting type: "name", "date", "size"
// sort.reverse boolean false Whether to reverse the sorting order // sort.descending boolean true Whether to reverse the sorting order
// Normal order: name: lexicographical, e.g. "a.jpg" before "b.jpg" // Normal order: name: lexicographical, e.g. "a.jpg" before "b.jpg"
// date: older before newer // date: older before newer
// size: smaller before larger // size: smaller before larger
@@ -57,12 +57,38 @@ namespace WallReel::Core::Config {
inline const QString s_DefaultConfigFileName = "config.json"; inline const QString s_DefaultConfigFileName = "config.json";
enum class SortType : int { enum class SortType : int {
None = 0, // "none"
Name, // "name" Name, // "name"
Date, // "date" Date, // "date"
Size, // "size" Size, // "size"
}; };
inline const QStringList s_availableSortTypes = {"Name", "Date", "Size"};
inline QString sortTypeToString(SortType type) {
switch (type) {
case SortType::Name:
return "Name";
case SortType::Date:
return "Date";
case SortType::Size:
return "Size";
default:
return "Date";
}
}
inline SortType stringToSortType(const QString& str) {
if (str.compare("name", Qt::CaseInsensitive) == 0) {
return SortType::Name;
} else if (str.compare("date", Qt::CaseInsensitive) == 0) {
return SortType::Date;
} else if (str.compare("size", Qt::CaseInsensitive) == 0) {
return SortType::Size;
} else {
return SortType::Date; // default
}
}
struct WallpaperConfigItems { struct WallpaperConfigItems {
struct WallpaperDirConfigItem { struct WallpaperDirConfigItem {
QString path; QString path;
@@ -118,8 +144,8 @@ struct StyleConfigItems {
}; };
struct SortConfigItems { struct SortConfigItems {
SortType type = SortType::Name; SortType type = SortType::Date;
bool reverse = false; bool descending = true;
}; };
} // namespace WallReel::Core::Config } // namespace WallReel::Core::Config
+4 -6
View File
@@ -298,9 +298,7 @@ void WallReel::Core::Config::Manager::_loadSortConfig(const QJsonObject& root) {
const auto& val = config["type"]; const auto& val = config["type"];
if (val.isString()) { if (val.isString()) {
QString type = val.toString().toLower(); QString type = val.toString().toLower();
if (type == "none") { if (type == "name") {
m_sortConfig.type = SortType::None;
} else if (type == "name") {
m_sortConfig.type = SortType::Name; m_sortConfig.type = SortType::Name;
} else if (type == "date") { } else if (type == "date") {
m_sortConfig.type = SortType::Date; m_sortConfig.type = SortType::Date;
@@ -311,10 +309,10 @@ void WallReel::Core::Config::Manager::_loadSortConfig(const QJsonObject& root) {
} }
} }
} }
if (config.contains("reverse")) { if (config.contains("descending")) {
const auto& val = config["reverse"]; const auto& val = config["descending"];
if (val.isBool()) { if (val.isBool()) {
m_sortConfig.reverse = val.toBool(); m_sortConfig.descending = val.toBool();
} }
} }
} }
-23
View File
@@ -19,12 +19,6 @@ namespace WallReel::Core::Config {
class Manager : public QObject { class Manager : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int imageWidth READ getImageWidth CONSTANT)
Q_PROPERTY(int imageHeight READ getImageHeight CONSTANT)
Q_PROPERTY(double imageFocusScale READ getImageFocusScale CONSTANT)
Q_PROPERTY(int windowWidth READ getWindowWidth CONSTANT)
Q_PROPERTY(int windowHeight READ getWindowHeight CONSTANT)
public: public:
/** /**
* @brief Construct a new Manager object * @brief Construct a new Manager object
@@ -63,23 +57,6 @@ class Manager : public QObject {
const SortConfigItems& getSortConfig() const { return m_sortConfig; } const SortConfigItems& getSortConfig() const { return m_sortConfig; }
// Getters for Q_PROPERTY
int getImageWidth() const { return m_styleConfig.imageWidth; }
int getImageHeight() const { return m_styleConfig.imageHeight; }
double getImageFocusScale() const { return m_styleConfig.imageFocusScale; }
int getWindowWidth() const { return m_styleConfig.windowWidth; }
int getWindowHeight() const { return m_styleConfig.windowHeight; }
/**
* @brief A quick snippet to get the focused image size as a QSize
*
* @return QSize
*/
QSize getFocusImageSize() const { QSize getFocusImageSize() const {
return QSize{m_styleConfig.imageWidth, m_styleConfig.imageHeight} * m_styleConfig.imageFocusScale; return QSize{m_styleConfig.imageWidth, m_styleConfig.imageHeight} * m_styleConfig.imageFocusScale;
} }
+113
View File
@@ -0,0 +1,113 @@
#include "manager.hpp"
#include <QFuture>
#include <QtConcurrent>
#include "data.hpp"
#include "logger.hpp"
WALLREEL_DECLARE_SENDER("ImageManager")
WallReel::Core::Image::Manager::Manager(
Cache::Manager& cacheMgr,
const QSize& thumbnailSize,
QObject* parent)
: QObject(parent),
m_cacheMgr(cacheMgr),
m_thumbnailSize(thumbnailSize) {
m_dataModel = new Model(this);
m_proxyModel = new ProxyModel(this);
m_proxyModel->setSourceModel(m_dataModel);
connect(
&m_watcher,
&QFutureWatcher<Data*>::finished,
this,
&Manager::_onProcessingFinished);
connect(
&m_progressUpdateTimer,
&QTimer::timeout,
this,
[this]() {
emit processedCountChanged();
});
}
WallReel::Core::Image::Manager::~Manager() {
m_watcher.cancel();
m_watcher.waitForFinished();
}
void WallReel::Core::Image::Manager::loadAndProcess(const QStringList& paths) {
if (m_isLoading) {
WR_WARN("Already loading images. Ignoring new load request.");
return;
}
m_isLoading = true;
emit isLoadingChanged();
_clearData();
m_processedCount = 0;
m_progressUpdateTimer.start(s_ProgressUpdateIntervalMs);
// These are all small objects so capturing by value should be fine
const auto thumbnailSize = m_thumbnailSize;
const auto counterPtr = &m_processedCount;
const auto cacheMgr = &m_cacheMgr;
QFuture<Data*> future =
QtConcurrent::mapped(paths, [thumbnailSize, counterPtr, cacheMgr](const QString& path) {
auto data = Data::create(path, thumbnailSize, *cacheMgr);
counterPtr->fetch_add(1, std::memory_order_relaxed);
return data;
});
m_watcher.setFuture(future);
emit totalCountChanged();
}
void WallReel::Core::Image::Manager::stop() {
if (m_isLoading) {
WR_INFO("Stopping image loading...");
m_watcher.cancel();
} else {
WR_WARN("No loading operation to stop.");
}
}
void WallReel::Core::Image::Manager::_clearData() {
m_dataModel->clearData();
}
void WallReel::Core::Image::Manager::_onProgressValueChanged(int value) {
Q_UNUSED(value);
emit processedCountChanged();
}
void WallReel::Core::Image::Manager::_onProcessingFinished() {
auto results = m_watcher.future().results();
QList<Data*> filteredResults;
filteredResults.reserve(results.size());
for (Data* data : results) {
if (data && data->isValid()) {
filteredResults.append(data);
m_dataMap.insert(data->getId(), data);
} else {
if (data) {
WR_WARN(QString("Failed to load image data for path '%1'").arg(data->getFullPath()));
delete data;
}
}
}
m_dataModel->insertData(filteredResults);
WR_INFO("Finished loading images. Total valid images: " + QString::number(filteredResults.size()));
m_isLoading = false;
m_progressUpdateTimer.stop();
emit processedCountChanged();
// QTimer::singleShot(s_IsLoadingUpdateIntervalMs, this, [this]() {
// emit isLoadingChanged();
// });
emit isLoadingChanged();
}
+91
View File
@@ -0,0 +1,91 @@
#ifndef WALLREEL_IMAGEMANAGER_HPP
#define WALLREEL_IMAGEMANAGER_HPP
#include <QAbstractListModel>
#include <QDir>
#include <QFutureWatcher>
#include <QTimer>
#include <atomic>
#include "Cache/manager.hpp"
#include "data.hpp"
#include "model.hpp"
namespace WallReel::Core::Image {
class Manager : public QObject {
Q_OBJECT
public:
// Constructor / Destructor
Manager(
Cache::Manager& cacheMgr,
const QSize& thumbnailSize,
QObject* parent = nullptr);
~Manager();
Image::ProxyModel* model() const { return m_proxyModel; }
bool isLoading() const { return m_isLoading; }
int processedCount() const { return m_processedCount.load(std::memory_order_relaxed); }
int totalCount() const { return m_watcher.progressMaximum(); }
void setSortType(Config::SortType type) { m_proxyModel->setSortType(type); }
void setSortDescending(bool descending) { m_proxyModel->setSortDescending(descending); }
void setSearchText(const QString& text) { m_proxyModel->setSearchText(text); }
Config::SortType sortType() const { return m_proxyModel->getSortType(); }
bool sortDescending() const { return m_proxyModel->isSortDescending(); }
QString searchText() const { return m_proxyModel->getSearchText(); }
void loadAndProcess(const QStringList& paths);
void stop();
Image::Data* imageAt(const QString& id) {
if (m_dataMap.contains(id)) {
return m_dataMap[id];
}
return nullptr;
}
private:
void _clearData();
signals:
// Properties
void isLoadingChanged();
void processedCountChanged();
void totalCountChanged();
private slots:
void _onProgressValueChanged(int value);
void _onProcessingFinished();
private:
Model* m_dataModel;
ProxyModel* m_proxyModel;
QHash<QString, Data*> m_dataMap;
Cache::Manager& m_cacheMgr;
QSize m_thumbnailSize;
QFutureWatcher<Data*> m_watcher;
bool m_isLoading = false;
std::atomic<int> m_processedCount{0};
QTimer m_progressUpdateTimer;
static constexpr int s_ProgressUpdateIntervalMs = 30;
};
} // namespace WallReel::Core::Image
#endif // WALLREEL_IMAGEMANAGER_HPP
+32 -306
View File
@@ -1,72 +1,32 @@
#include "model.hpp" #include "model.hpp"
#include <QFuture>
#include <QtConcurrent>
#include "data.hpp"
#include "logger.hpp" #include "logger.hpp"
WALLREEL_DECLARE_SENDER("ImageModel") WALLREEL_DECLARE_SENDER("ImageModel")
WallReel::Core::Image::Model::Model( namespace WallReel::Core::Image {
const Config::SortConfigItems& sortConfig,
Cache::Manager& cacheMgr,
const QSize& thumbnailSize,
QObject* parent)
: QAbstractListModel(parent),
m_sortConfig(sortConfig),
m_cacheMgr(cacheMgr),
m_thumbnailSize(thumbnailSize),
m_currentSortType(sortConfig.type),
m_currentSortReverse(sortConfig.reverse) {
connect(
&m_watcher,
&QFutureWatcher<Data*>::finished,
this,
&Model::_onProcessingFinished);
connect(
&m_progressUpdateTimer,
&QTimer::timeout,
this,
[this]() {
emit progressChanged();
});
// Pipeline: sort -> filter -> update properties Model::Model(QObject* parent) : QAbstractListModel(parent) {}
connect(this, &Model::currentSortTypeChanged, this, &Model::_onSortMethodChanged);
connect(this, &Model::currentSortReverseChanged, this, &Model::_onSortMethodChanged);
connect(this, &Model::searchTextChanged, this, &Model::_onSearchTextChanged);
connect(this, &Model::focusedImageChanged, this, &Model::_updateFocusedProperties);
m_sortIndices.resize(4); // None, Name, Date, Size Model::~Model() {
}
WallReel::Core::Image::Model::~Model() {
m_watcher.cancel();
m_watcher.waitForFinished();
qDeleteAll(m_data); qDeleteAll(m_data);
m_data.clear();
} }
int WallReel::Core::Image::Model::rowCount(const QModelIndex& parent) const { int Model::rowCount(const QModelIndex& parent) const {
if (parent.isValid()) { if (parent.isValid()) {
return 0; return 0;
} }
return m_filteredIndices.count(); return m_data.count();
} }
QVariant WallReel::Core::Image::Model::data(const QModelIndex& index, int role) const { QVariant Model::data(const QModelIndex& index, int role) const {
if (!index.isValid() || index.row() >= m_filteredIndices.count()) { if (!index.isValid() || index.row() >= m_data.count() || index.row() < 0) {
WR_DEBUG("Invalid index requested: " + QString::number(index.row())); WR_DEBUG("Invalid index requested: " + QString::number(index.row()));
return QVariant(); return QVariant();
} }
int actualIndex = _convertProxyIndex(index.row()); const auto& item = m_data[index.row()];
if (actualIndex < 0 || actualIndex >= m_data.count()) {
WR_DEBUG("Actual index out of bounds: " + QString::number(actualIndex));
return QVariant();
}
// WR_DEBUG("Data requested for index: " + QString::number(index.row()) + ", actual index: " + QString::number(actualIndex) + ", role: " + QString::number(role));
const auto& item = m_data[actualIndex];
switch (role) { switch (role) {
case IdRole: case IdRole:
return item->getId(); return item->getId();
@@ -76,89 +36,24 @@ QVariant WallReel::Core::Image::Model::data(const QModelIndex& index, int role)
return item->getFullPath(); return item->getFullPath();
case NameRole: case NameRole:
return item->getFileName(); return item->getFileName();
case SizeRole:
return item->getSize();
case DateRole:
return item->getLastModified();
case DomColorRole:
return item->getDominantColor();
default: default:
return QVariant(); return QVariant();
} }
} }
QString WallReel::Core::Image::Model::currentSortType() const { QVariant Model::dataAt(int index, const QString& roleName) const {
switch (m_currentSortType) { if (index < 0 || index >= m_data.count()) {
case Config::SortType::None:
return "None";
case Config::SortType::Name:
return "Name";
case Config::SortType::Date:
return "Date";
case Config::SortType::Size:
return "Size";
default:
return "Unknown";
}
}
void WallReel::Core::Image::Model::setCurrentSortType(const QString& type) {
Config::SortType newSortType = Config::SortType::None;
if (type == "None") {
newSortType = Config::SortType::None;
} else if (type == "Name") {
newSortType = Config::SortType::Name;
} else if (type == "Date") {
newSortType = Config::SortType::Date;
} else if (type == "Size") {
newSortType = Config::SortType::Size;
}
if (m_currentSortType != newSortType) {
m_currentSortType = newSortType;
emit currentSortTypeChanged();
}
}
void WallReel::Core::Image::Model::setCurrentSortReverse(bool reverse) {
if (m_currentSortReverse != reverse) {
m_currentSortReverse = reverse;
emit currentSortReverseChanged();
}
}
void WallReel::Core::Image::Model::setSearchText(const QString& text) {
// WR_DEBUG("Search text changed: " + text);
if (m_searchText != text) {
m_searchText = text;
_applySearchFilter();
emit searchTextChanged();
}
}
WallReel::Core::Image::Data* WallReel::Core::Image::Model::imageAt(int index) {
if (index < 0 || index >= m_filteredIndices.count()) {
WR_DEBUG("Invalid index requested: " + QString::number(index));
return nullptr;
}
int actualIndex = _convertProxyIndex(index);
if (actualIndex < 0 || actualIndex >= m_data.count()) {
WR_DEBUG("Actual index out of bounds: " + QString::number(actualIndex));
return nullptr;
}
return m_data[actualIndex];
}
WallReel::Core::Image::Data* WallReel::Core::Image::Model::focusedImage() {
return imageAt(m_focusedIndex);
}
QVariant WallReel::Core::Image::Model::dataAt(int index, const QString& roleName) const {
if (index < 0 || index >= m_filteredIndices.count()) {
WR_DEBUG("Invalid index requested: " + QString::number(index)); WR_DEBUG("Invalid index requested: " + QString::number(index));
return QVariant(); return QVariant();
} }
int actualIndex = _convertProxyIndex(index); const auto& item = m_data[index];
if (actualIndex < 0 || actualIndex >= m_data.count()) {
WR_DEBUG("Actual index out of bounds: " + QString::number(actualIndex));
return QVariant();
}
const auto& item = m_data[actualIndex];
if (roleName == "imgId") { if (roleName == "imgId") {
return item->getId(); return item->getId();
} else if (roleName == "imgUrl") { } else if (roleName == "imgUrl") {
@@ -167,203 +62,34 @@ QVariant WallReel::Core::Image::Model::dataAt(int index, const QString& roleName
return item->getFullPath(); return item->getFullPath();
} else if (roleName == "imgName") { } else if (roleName == "imgName") {
return item->getFileName(); return item->getFileName();
} else if (roleName == "imgSize") {
return item->getSize();
} else if (roleName == "imgDate") {
return item->getLastModified();
} else if (roleName == "imgDomColor") {
return item->getDominantColor();
} else { } else {
return QVariant(); return QVariant();
} }
} }
void WallReel::Core::Image::Model::loadAndProcess(const QStringList& paths) { void Model::insertData(const QList<Data*>& newData) {
if (m_isLoading) { if (newData.isEmpty()) {
WR_WARN("Already loading images. Ignoring new load request.");
return; return;
} }
m_isLoading = true; beginInsertRows(QModelIndex(), m_data.count(), m_data.count() + newData.count() - 1);
emit isLoadingChanged(); m_data.append(newData);
endInsertRows();
_clearData();
m_processedCount = 0;
m_progressUpdateTimer.start(s_ProgressUpdateIntervalMs);
// These are all small objects so capturing by value should be fine
const auto thumbnailSize = m_thumbnailSize;
const auto counterPtr = &m_processedCount;
const auto cacheMgr = &m_cacheMgr;
QFuture<Data*> future =
QtConcurrent::mapped(paths, [thumbnailSize, counterPtr, cacheMgr](const QString& path) {
auto data = Data::create(path, thumbnailSize, *cacheMgr);
counterPtr->fetch_add(1, std::memory_order_relaxed);
return data;
});
m_watcher.setFuture(future);
emit totalCountChanged();
} }
void WallReel::Core::Image::Model::focusOnIndex(int index) { void Model::clearData() {
if (index < 0 || index >= m_filteredIndices.count()) { if (m_data.isEmpty()) {
WR_DEBUG("Invalid index to focus on: " + QString::number(index));
return; return;
} }
int actualIndex = _convertProxyIndex(index);
if (actualIndex < 0 || actualIndex >= m_data.count()) {
WR_DEBUG("Actual index out of bounds for focus: " + QString::number(actualIndex));
return;
}
if (m_focusedIndex != index) {
m_focusedIndex = index;
emit focusedIndexChanged();
emit focusedImageChanged();
_updateFocusedProperties();
}
}
void WallReel::Core::Image::Model::stop() {
if (m_isLoading) {
WR_INFO("Stopping image loading...");
m_watcher.cancel();
} else {
WR_WARN("No loading operation to stop.");
}
}
int WallReel::Core::Image::Model::_convertProxyIndex(int proxyIndex) const {
if (proxyIndex < 0 || proxyIndex >= m_filteredIndices.size()) {
WR_DEBUG("Invalid proxy index requested: " + QString::number(proxyIndex));
return -1;
}
return m_filteredIndices[proxyIndex];
}
void WallReel::Core::Image::Model::_clearData() {
beginResetModel(); beginResetModel();
qDeleteAll(m_data); qDeleteAll(m_data);
m_data.clear(); m_data.clear();
for (auto& i : m_sortIndices) {
i.clear();
}
m_filteredIndices.clear();
endResetModel(); endResetModel();
} }
void WallReel::Core::Image::Model::_updateSortIndices(Config::SortType type) { } // namespace WallReel::Core::Image
QList<int>& indices = m_sortIndices[static_cast<int>(type)];
indices.resize(m_data.count());
std::iota(indices.begin(), indices.end(), 0);
if (type == Config::SortType::None) {
return;
}
const auto& compareFunc = [this, type](int idx1, int idx2) {
const Data* data1 = m_data[idx1];
const Data* data2 = m_data[idx2];
if (!data1 || !data2) {
return false;
}
switch (type) {
case Config::SortType::Name:
return QString::compare(data1->getFileName(), data2->getFileName(), Qt::CaseInsensitive) < 0;
case Config::SortType::Date:
return data1->getLastModified() < data2->getLastModified();
case Config::SortType::Size:
return data1->getSize() < data2->getSize();
default:
return false;
}
};
std::sort(indices.begin(), indices.end(), compareFunc);
}
void WallReel::Core::Image::Model::_updateFocusedProperties() {
if (m_focusedIndex < 0 || m_focusedIndex >= m_filteredIndices.size()) {
m_focusedName = "";
} else {
int actualIndex = _convertProxyIndex(m_focusedIndex);
if (actualIndex < 0 || actualIndex >= m_data.count()) {
m_focusedName = "";
} else {
const auto& item = m_data[actualIndex];
m_focusedName = item->getFileName();
}
}
emit focusedNameChanged();
}
void WallReel::Core::Image::Model::_applySearchFilter(bool informView) {
const auto& sortedIndices = m_sortIndices[static_cast<int>(m_currentSortType)];
int srcPos = 0, resPos = 0;
for (; srcPos < sortedIndices.size(); ++srcPos) {
int actualIndex = m_currentSortReverse ? sortedIndices[sortedIndices.size() - 1 - srcPos] : sortedIndices[srcPos];
const auto& item = m_data[actualIndex];
if (item->getFileName().contains(m_searchText, Qt::CaseInsensitive)) {
if (resPos >= m_filteredIndices.size() || m_filteredIndices[resPos] != actualIndex) {
break;
}
resPos++;
}
}
if (resPos == m_filteredIndices.size() && srcPos == sortedIndices.size()) {
return; // No change in filtered results
}
if (informView) {
emit layoutAboutToBeChanged();
}
m_filteredIndices.resize(resPos);
for (int i = srcPos; i < sortedIndices.size(); ++i) {
int actualIndex = m_currentSortReverse ? sortedIndices[sortedIndices.size() - 1 - i] : sortedIndices[i];
const auto& item = m_data[actualIndex];
if (item->getFileName().contains(m_searchText, Qt::CaseInsensitive)) {
m_filteredIndices.append(actualIndex);
}
}
if (informView) {
emit layoutChanged();
}
}
void WallReel::Core::Image::Model::_onProgressValueChanged(int value) {
Q_UNUSED(value);
emit progressChanged();
}
void WallReel::Core::Image::Model::_onProcessingFinished() {
auto results = m_watcher.future().results();
beginResetModel();
for (auto& data : results) {
if (data && data->isValid()) {
m_data.append(data);
} else {
WR_WARN("Failed to load image: " + (data ? data->getFullPath() : "null"));
delete data;
data = nullptr;
}
}
for (int i = 0; i < m_sortIndices.size(); ++i) {
_updateSortIndices(static_cast<Config::SortType>(i));
}
_applySearchFilter(false);
endResetModel();
WR_INFO("Finished loading images. Total valid images: " + QString::number(m_data.count()));
m_isLoading = false;
m_progressUpdateTimer.stop();
emit progressChanged();
// QTimer::singleShot(s_IsLoadingUpdateIntervalMs, this, [this]() {
// emit isLoadingChanged();
// });
emit isLoadingChanged();
}
void WallReel::Core::Image::Model::_onSortMethodChanged() {
_applySearchFilter();
emit focusedImageChanged();
}
void WallReel::Core::Image::Model::_onSearchTextChanged() {
emit focusedImageChanged();
}
+35 -146
View File
@@ -2,75 +2,25 @@
#define WALLREEL_IMAGEMODEL_HPP #define WALLREEL_IMAGEMODEL_HPP
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDir> #include <QSortFilterProxyModel>
#include <QFutureWatcher>
#include <QTimer>
#include <atomic>
#include "Cache/manager.hpp"
#include "Config/data.hpp" #include "Config/data.hpp"
#include "data.hpp" #include "data.hpp"
// Development note
/*
What "Proxy index" is:
There are currently three layers of indices in the Model:
1. Actual index: The index of the image in the original data list (m_data), the order is not
guaranteed and can be considered random.
2. Sorted index: The index of the image after sorting, which is stored in m_sortIndices based on
different sort types. m_sortIndices are precomputed and does not change unless
m_data changes. In practice, the choise of which mapping from m_sortIndices to use
is determined by m_currentSortType.
3. Filtered index: The final mapping from the index exposed to the QML view to the actual data index,
which is stored in m_filteredIndices. m_filteredIndices is updated each time when
the sort type / sort order / search text changes, and only informs the layout to
update when its content actually changes.
Therefore, when acquiring data, the "proxied" index must first be converted to the "actual" index
by looking up m_filteredIndices, and then the actual data can be accessed from m_data.
*/
namespace WallReel::Core::Image { namespace WallReel::Core::Image {
/**
* @brief An unrefactored (view)model class that manages and provides the image list and properties of the focused image.
*
*/
class Model : public QAbstractListModel { class Model : public QAbstractListModel {
Q_OBJECT Q_OBJECT
// Controls which of the main screen and the loading screen should be shown
// and triggers callbacks when loading finished
Q_PROPERTY(bool isLoading READ isLoading NOTIFY isLoadingChanged)
// Indicates the progress of loading, used to update the progress bar in the loading screen
// Not neccessarily updated on every image loaded, but should be updated frequently enough to make the progress bar smooth
Q_PROPERTY(int processedCount READ processedCount NOTIFY progressChanged)
// Total count of images to be loaded, used to calculate the progress percentage
Q_PROPERTY(int totalCount READ totalCount NOTIFY totalCountChanged)
// Current index
Q_PROPERTY(int currentIndex READ focusedIndex WRITE focusOnIndex NOTIFY focusedIndexChanged)
// Sorting related properties
// How this works:
// 1. User interact with QML control components
// 2. QML calls the setter of the corresponding property in the Model
// 3. Model changes its internal state and update the sort indices accordingly
// 4. Model emits signal and possibly update state on QML side (for stateless controls)
// 5. ... Continue on further updates (search filter / focused image properties / etc)
Q_PROPERTY(QString currentSortType READ currentSortType WRITE setCurrentSortType NOTIFY currentSortTypeChanged)
Q_PROPERTY(bool currentSortReverse READ currentSortReverse WRITE setCurrentSortReverse NOTIFY currentSortReverseChanged)
// Focused image related properties, updated when focused image changed
Q_PROPERTY(QString focusedName READ focusedName NOTIFY focusedNameChanged)
public: public:
// Types
enum Roles { enum Roles {
IdRole = Qt::UserRole + 1, IdRole = Qt::UserRole + 1,
UrlRole, UrlRole,
PathRole, PathRole,
NameRole NameRole,
SizeRole,
DateRole,
DomColorRole,
}; };
QHash<int, QByteArray> roleNames() const override { QHash<int, QByteArray> roleNames() const override {
@@ -79,118 +29,57 @@ class Model : public QAbstractListModel {
{UrlRole, "imgUrl"}, // file:///... {UrlRole, "imgUrl"}, // file:///...
{PathRole, "imgPath"}, {PathRole, "imgPath"},
{NameRole, "imgName"}, {NameRole, "imgName"},
{SizeRole, "imgSize"},
{DateRole, "imgDate"},
{DomColorRole, "imgDomColor"},
}; };
} }
// Constructor / Destructor explicit Model(QObject* parent = nullptr);
Model(
const Config::SortConfigItems& sortConfig,
Cache::Manager& cacheMgr,
const QSize& thumbnailSize,
QObject* parent = nullptr);
~Model(); ~Model();
// QAbstractListModel
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
// Properties QVariant dataAt(int index, const QString& roleName) const;
bool isLoading() const { return m_isLoading; } void clearData();
int processedCount() const { return m_processedCount.load(std::memory_order_relaxed); } void insertData(const QList<Data*>& newData);
int totalCount() const { return m_watcher.progressMaximum(); }
int focusedIndex() const { return m_focusedIndex; }
QString currentSortType() const;
void setCurrentSortType(const QString& type);
bool currentSortReverse() const { return m_currentSortReverse; }
void setCurrentSortReverse(bool reverse);
QString focusedName() const { return m_focusedName; }
// Methods
Q_INVOKABLE void setSearchText(const QString& text);
Data* imageAt(int index);
Data* focusedImage();
Q_INVOKABLE QVariant dataAt(int index, const QString& roleName) const;
void loadAndProcess(const QStringList& paths);
void focusOnIndex(int index);
Q_INVOKABLE void stop();
private: private:
int _convertProxyIndex(int proxyIndex) const;
void _clearData();
// Update the corresponding mapping in m_sortIndices based on the current m_data and the given sort type
void _updateSortIndices(Config::SortType type);
// Reobtain the properties of the focused image and emit corresponding signals
void _updateFocusedProperties();
// Update m_filteredIndices, only calls layoutAboutToBeChanged and layoutChanged when the filtered result
// actually changes and informView is true
void _applySearchFilter(bool informView = true);
signals:
// Properties
void isLoadingChanged();
void progressChanged();
void totalCountChanged();
void focusedIndexChanged();
void currentSortTypeChanged(); // -> _onSortMethodChanged
void currentSortReverseChanged(); // -> _onSortMethodChanged
void focusedNameChanged();
// emitted after search text changed and the filter is applied
void searchTextChanged(); // -> _onSearchTextChanged
// emiited when the focued image (is believed to be) changed
void focusedImageChanged();
private slots:
void _onProgressValueChanged(int value);
void _onProcessingFinished();
void _onSortMethodChanged();
void _onSearchTextChanged();
private:
const Config::SortConfigItems& m_sortConfig;
Cache::Manager& m_cacheMgr;
QSize m_thumbnailSize;
QList<Data*> m_data; QList<Data*> m_data;
};
QList<QList<int>> m_sortIndices; class ProxyModel : public QSortFilterProxyModel {
Config::SortType m_currentSortType; Q_OBJECT
bool m_currentSortReverse;
QString m_focusedName{}; public:
explicit ProxyModel(QObject* parent = nullptr);
QList<int> m_filteredIndices; void setSearchText(const QString& text);
QString m_searchText{};
// QTimer m_searchDebounceTimer;
// static constexpr int s_SearchDebounceIntervalMs = 300;
QFutureWatcher<Data*> m_watcher; QString getSearchText() const { return m_searchText; }
bool m_isLoading = false;
int m_focusedIndex = -1; void setSortType(Config::SortType type);
std::atomic<int> m_processedCount{0}; Config::SortType getSortType() const { return m_sortType; }
QTimer m_progressUpdateTimer;
static constexpr int s_ProgressUpdateIntervalMs = 30; void setSortDescending(bool descending);
bool isSortDescending() const { return m_sortDescending; }
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override;
private:
QString m_searchText;
Config::SortType m_sortType;
bool m_sortDescending;
}; };
} // namespace WallReel::Core::Image } // namespace WallReel::Core::Image
+67
View File
@@ -0,0 +1,67 @@
#include "model.hpp"
namespace WallReel::Core::Image {
ProxyModel::ProxyModel(QObject* parent)
: QSortFilterProxyModel(parent) {
setFilterCaseSensitivity(Qt::CaseInsensitive);
setDynamicSortFilter(true);
}
void ProxyModel::setSearchText(const QString& text) {
if (m_searchText != text) {
beginFilterChange();
m_searchText = text;
setFilterFixedString(text);
endFilterChange();
}
}
void ProxyModel::setSortType(Config::SortType type) {
if (m_sortType != type) {
m_sortType = type;
invalidate();
sort(0, m_sortDescending ? Qt::DescendingOrder : Qt::AscendingOrder);
}
}
void ProxyModel::setSortDescending(bool descending) {
if (m_sortDescending != descending) {
m_sortDescending = descending;
sort(0, m_sortDescending ? Qt::DescendingOrder : Qt::AscendingOrder);
}
}
bool ProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const {
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
QString imageName = sourceModel()->data(index, Model::NameRole).toString();
if (m_searchText.isEmpty()) return true;
return imageName.contains(m_searchText, Qt::CaseInsensitive);
}
bool ProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const {
switch (m_sortType) {
case Config::SortType::Name: {
QString leftName = sourceModel()->data(source_left, Model::NameRole).toString();
QString rightName = sourceModel()->data(source_right, Model::NameRole).toString();
return leftName < rightName;
}
case Config::SortType::Date: {
QDateTime leftDate = sourceModel()->data(source_left, Model::DateRole).toDateTime();
QDateTime rightDate = sourceModel()->data(source_right, Model::DateRole).toDateTime();
return leftDate < rightDate;
}
case Config::SortType::Size: {
qint64 leftSize = sourceModel()->data(source_left, Model::SizeRole).toLongLong();
qint64 rightSize = sourceModel()->data(source_right, Model::SizeRole).toLongLong();
return leftSize < rightSize;
}
default:
qDebug() << "Unknown sort type:" << static_cast<int>(m_sortType);
return false;
}
}
} // namespace WallReel::Core::Image
+6 -6
View File
@@ -1,5 +1,7 @@
#include "manager.hpp" #include "manager.hpp"
#include <qcontainerfwd.h>
#include "Palette/matchcolor.hpp" #include "Palette/matchcolor.hpp"
#include "Utils/misc.hpp" #include "Utils/misc.hpp"
#include "logger.hpp" #include "logger.hpp"
@@ -9,10 +11,8 @@ WALLREEL_DECLARE_SENDER("PaletteManager")
WallReel::Core::Palette::Manager::Manager( WallReel::Core::Palette::Manager::Manager(
const Config::ThemeConfigItems& config, const Config::ThemeConfigItems& config,
Image::Model& imageModel, Image::Manager& imageManager,
QObject* parent) : QObject(parent), m_imageModel(imageModel) { QObject* parent) : QObject(parent), m_imageManager(imageManager) {
connect(&m_imageModel, &Image::Model::focusedImageChanged, this, &Manager::updateColor);
// The new ones overrides the old ones, use a hashtable to track // The new ones overrides the old ones, use a hashtable to track
// the latest index of each palette name, then only insert the // the latest index of each palette name, then only insert the
// ones whose index matches the latest index in the hashtable // ones whose index matches the latest index in the hashtable
@@ -64,7 +64,7 @@ WallReel::Core::Palette::Manager::Manager(
} }
} }
void WallReel::Core::Palette::Manager::updateColor() { void WallReel::Core::Palette::Manager::updateColor(const QString& imageId) {
bool hasResult = false; bool hasResult = false;
Utils::Defer defer([&]() { Utils::Defer defer([&]() {
if (!hasResult) { if (!hasResult) {
@@ -74,7 +74,7 @@ void WallReel::Core::Palette::Manager::updateColor() {
emit colorChanged(); emit colorChanged();
emit colorNameChanged(); emit colorNameChanged();
}); });
auto imageData = m_imageModel.focusedImage(); auto imageData = m_imageManager.imageAt(imageId);
if (!imageData || !imageData->isValid()) { if (!imageData || !imageData->isValid()) {
WR_WARN("No valid focused image data. Cannot update palette color."); WR_WARN("No valid focused image data. Cannot update palette color.");
return; return;
+6 -13
View File
@@ -4,22 +4,17 @@
#include <qcolor.h> #include <qcolor.h>
#include "Config/data.hpp" #include "Config/data.hpp"
#include "Image/model.hpp" #include "Image/manager.hpp"
#include "data.hpp" #include "data.hpp"
namespace WallReel::Core::Palette { namespace WallReel::Core::Palette {
class Manager : public QObject { class Manager : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QList<PaletteItem> availablePalettes READ availablePalettes CONSTANT)
Q_PROPERTY(QVariant selectedPalette READ selectedPalette WRITE setSelectedPalette NOTIFY selectedPaletteChanged)
Q_PROPERTY(QVariant selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged)
Q_PROPERTY(QColor color READ color NOTIFY colorChanged)
Q_PROPERTY(QString colorName READ colorName NOTIFY colorNameChanged)
public: public:
Manager(const Config::ThemeConfigItems& config, Manager(const Config::ThemeConfigItems& config,
Image::Model& imageModel, Image::Manager& imageManager,
QObject* parent = nullptr); QObject* parent = nullptr);
// Properties // Properties
@@ -44,7 +39,7 @@ class Manager : public QObject {
return QVariant(); return QVariant();
} }
Q_INVOKABLE void setSelectedPalette(const QVariant& paletteVar) { void setSelectedPalette(const QVariant& paletteVar) {
if (paletteVar.isNull() || !paletteVar.isValid()) { if (paletteVar.isNull() || !paletteVar.isValid()) {
m_selectedPalette = std::nullopt; m_selectedPalette = std::nullopt;
} else { } else {
@@ -53,17 +48,15 @@ class Manager : public QObject {
m_selectedColor = std::nullopt; m_selectedColor = std::nullopt;
emit selectedPaletteChanged(); emit selectedPaletteChanged();
emit selectedColorChanged(); emit selectedColorChanged();
updateColor();
} }
Q_INVOKABLE void setSelectedColor(const QVariant& colorVar) { void setSelectedColor(const QVariant& colorVar) {
if (colorVar.isNull() || !colorVar.isValid()) { if (colorVar.isNull() || !colorVar.isValid()) {
m_selectedColor = std::nullopt; m_selectedColor = std::nullopt;
} else { } else {
m_selectedColor = colorVar.value<ColorItem>(); m_selectedColor = colorVar.value<ColorItem>();
} }
emit selectedColorChanged(); emit selectedColorChanged();
updateColor();
} }
// Getters // Getters
@@ -99,7 +92,7 @@ class Manager : public QObject {
} }
public slots: public slots:
void updateColor(); // <- Image::Model::focusedImageChanged void updateColor(const QString& imageId);
signals: signals:
void selectedPaletteChanged(); void selectedPaletteChanged();
@@ -108,7 +101,7 @@ class Manager : public QObject {
void colorNameChanged(); void colorNameChanged();
private: private:
Image::Model& m_imageModel; Image::Manager& m_imageManager;
QList<PaletteItem> m_palettes; QList<PaletteItem> m_palettes;
// Null means auto // Null means auto
+66
View File
@@ -0,0 +1,66 @@
#ifndef WALLREEL_PROVIDER_BOOTSTRAP_HPP
#define WALLREEL_PROVIDER_BOOTSTRAP_HPP
#include <QQmlEngine>
#include "Cache/manager.hpp"
#include "Config/manager.hpp"
#include "Image/manager.hpp"
#include "Palette/manager.hpp"
#include "Service/manager.hpp"
#include "Utils/misc.hpp"
#include "appoptions.hpp"
namespace WallReel::Core::Provider {
class Bootstrap {
friend class Carousel;
public:
Bootstrap(const AppOptions& options) {
cacheMgr = new Cache::Manager(Utils::getCacheDir());
if (options.clearCache) {
cacheMgr->clearCache();
return;
}
configMgr = new Config::Manager(
Utils::getConfigDir(),
options.appendDirs,
options.configPath);
imageMgr = new Image::Manager(
*cacheMgr,
configMgr->getFocusImageSize());
paletteMgr = new Palette::Manager(
configMgr->getThemeConfig(),
*imageMgr);
qRegisterMetaType<Palette::PaletteItem>("PaletteItem");
qRegisterMetaType<Palette::ColorItem>("ColorItem");
ServiceMgr = new Service::Manager(
configMgr->getActionConfig(),
*imageMgr,
*paletteMgr);
}
~Bootstrap() {
delete ServiceMgr;
delete paletteMgr;
delete imageMgr;
delete configMgr;
delete cacheMgr;
}
private:
Cache::Manager* cacheMgr;
Config::Manager* configMgr;
Image::Manager* imageMgr;
Palette::Manager* paletteMgr;
Service::Manager* ServiceMgr;
};
} // namespace WallReel::Core::Provider
#endif // WALLREEL_PROVIDER_BOOTSTRAP_HPP
+274
View File
@@ -0,0 +1,274 @@
#ifndef WALLREEL_PROVIDER_CAROUSEL_HPP
#define WALLREEL_PROVIDER_CAROUSEL_HPP
#include <qapplication.h>
#include <QApplication>
#include "Config/data.hpp"
#include "Config/manager.hpp"
#include "Image/manager.hpp"
#include "Palette/data.hpp"
#include "Palette/manager.hpp"
#include "Service/manager.hpp"
#include "bootstrap.hpp"
namespace WallReel::Core::Provider {
class Carousel : public QObject {
Q_OBJECT
// Image::Manager
public:
Q_PROPERTY(Image::ProxyModel* imageModel READ imageModel CONSTANT)
Q_PROPERTY(bool isLoading READ isLoading NOTIFY isLoadingChanged)
Q_PROPERTY(int processedCount READ processedCount NOTIFY processedCountChanged)
Q_PROPERTY(int totalCount READ totalCount NOTIFY totalCountChanged)
Q_PROPERTY(QString sortType READ sortType NOTIFY sortTypeChanged)
Q_PROPERTY(bool sortDescending READ sortDescending NOTIFY sortDescendingChanged)
Q_PROPERTY(QString searchText READ searchText NOTIFY searchTextChanged)
Image::ProxyModel* imageModel() const { return m_imageMgr->model(); }
bool isLoading() const { return m_imageMgr->isLoading(); }
int processedCount() const { return m_imageMgr->processedCount(); }
QString sortType() const { return Config::sortTypeToString(m_imageMgr->sortType()); }
bool sortDescending() const { return m_imageMgr->sortDescending(); }
QString searchText() const { return m_imageMgr->searchText(); }
int totalCount() const { return m_imageMgr->totalCount(); }
Q_INVOKABLE void stopLoading() { m_imageMgr->stop(); }
Q_INVOKABLE void setSortType(const QString& sortTypeStr) {
auto newSortType = Config::stringToSortType(sortTypeStr);
m_imageMgr->setSortType(newSortType);
emit sortTypeChanged();
}
Q_INVOKABLE void setSortType(Config::SortType sortType) {
m_imageMgr->setSortType(sortType);
emit sortTypeChanged();
}
Q_INVOKABLE void setSortDescending(bool descending) {
m_imageMgr->setSortDescending(descending);
emit sortDescendingChanged();
}
Q_INVOKABLE void setSearchText(const QString& text) {
m_imageMgr->setSearchText(text);
emit searchTextChanged();
}
signals:
void isLoadingChanged();
void processedCountChanged();
void totalCountChanged();
void sortTypeChanged();
void sortDescendingChanged();
void searchTextChanged();
// Config::Manager
public:
Q_PROPERTY(int imageWidth READ imageWidth CONSTANT)
Q_PROPERTY(int imageHeight READ imageHeight CONSTANT)
Q_PROPERTY(double imageFocusScale READ imageFocusScale CONSTANT)
Q_PROPERTY(int windowWidth READ windowWidth CONSTANT)
Q_PROPERTY(int windowHeight READ windowHeight CONSTANT)
Q_PROPERTY(QStringList availableSortTypes READ availableSortTypes CONSTANT)
int imageWidth() const { return m_configMgr->getStyleConfig().imageWidth; }
int imageHeight() const { return m_configMgr->getStyleConfig().imageHeight; }
int windowWidth() const { return m_configMgr->getStyleConfig().windowWidth; }
int windowHeight() const { return m_configMgr->getStyleConfig().windowHeight; }
double imageFocusScale() const { return m_configMgr->getStyleConfig().imageFocusScale; }
QStringList availableSortTypes() const { return Config::s_availableSortTypes; }
// Palette::Manager
public:
Q_PROPERTY(QList<Palette::PaletteItem> availablePalettes READ availablePalettes CONSTANT)
Q_PROPERTY(QVariant selectedPalette READ selectedPalette NOTIFY selectedPaletteChanged)
Q_PROPERTY(QVariant selectedColor READ selectedColor NOTIFY selectedColorChanged)
Q_PROPERTY(QColor color READ color NOTIFY colorChanged)
Q_PROPERTY(QString colorName READ colorName NOTIFY colorNameChanged)
QList<Palette::PaletteItem> availablePalettes() const { return m_paletteMgr->availablePalettes(); }
QVariant selectedPalette() const { return m_paletteMgr->selectedPalette(); }
QVariant selectedColor() const { return m_paletteMgr->selectedColor(); }
QColor color() const { return m_paletteMgr->color(); }
QString colorName() const { return m_paletteMgr->colorName(); }
Q_INVOKABLE void requestSelectPalette(const QVariant& palette) { m_paletteMgr->setSelectedPalette(palette); }
Q_INVOKABLE void requestSelectColor(const QVariant& color) { m_paletteMgr->setSelectedColor(color); }
signals:
void selectedPaletteChanged();
void selectedColorChanged();
void colorChanged();
void colorNameChanged();
// Service::Manager
public:
Q_PROPERTY(bool isProcessing READ isProcessing NOTIFY isProcessingChanged)
bool isProcessing() const { return m_serviceMgr->isProcessing(); }
Q_INVOKABLE void confirm() {
if (!m_currentImageId.isEmpty()) {
m_serviceMgr->selectWallpaper(m_currentImageId);
}
}
Q_INVOKABLE void cancel() { m_serviceMgr->cancel(); }
Q_INVOKABLE void restore() { m_serviceMgr->restore(); }
Q_INVOKABLE bool hasSelected() const { return m_serviceMgr->hasSelected(); }
signals:
void isProcessingChanged();
void selectCompleted();
void previewCompleted();
void restoreCompleted();
void cancelCompleted();
// Other states
public:
Q_PROPERTY(QString currentImageId READ currentImageId NOTIFY currentImageIdChanged)
Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged)
QString currentImageId() const { return m_currentImageId; }
int currentIndex() const { return m_currentIndex; }
Q_INVOKABLE void setCurrentImageId(const QString& imageId) {
if (m_currentImageId != imageId) {
m_currentImageId = imageId;
emit currentImageIdChanged();
}
}
Q_INVOKABLE void setCurrentIndex(int index) {
if (m_currentIndex != index) {
m_currentIndex = index;
emit currentIndexChanged();
}
}
signals:
void currentImageIdChanged();
void currentIndexChanged();
private:
QString m_currentImageId;
int m_currentIndex = -1;
// instances
public:
Carousel(QApplication* app,
Bootstrap& bootstrap,
QObject* parent = nullptr)
: QObject(parent),
m_configMgr(bootstrap.configMgr),
m_imageMgr(bootstrap.imageMgr),
m_paletteMgr(bootstrap.paletteMgr),
m_serviceMgr(bootstrap.ServiceMgr) {
// Simply forward signals
connect(m_imageMgr, &Image::Manager::isLoadingChanged, this, &Carousel::isLoadingChanged);
connect(m_imageMgr, &Image::Manager::processedCountChanged, this, &Carousel::processedCountChanged);
connect(m_imageMgr, &Image::Manager::totalCountChanged, this, &Carousel::totalCountChanged);
connect(m_paletteMgr, &Palette::Manager::selectedPaletteChanged, this, &Carousel::selectedPaletteChanged);
connect(m_paletteMgr, &Palette::Manager::selectedColorChanged, this, &Carousel::selectedColorChanged);
connect(m_paletteMgr, &Palette::Manager::colorChanged, this, &Carousel::colorChanged);
connect(m_paletteMgr, &Palette::Manager::colorNameChanged, this, &Carousel::colorNameChanged);
connect(m_serviceMgr, &Service::Manager::isProcessingChanged, this, &Carousel::isProcessingChanged);
connect(m_serviceMgr, &Service::Manager::selectCompleted, this, &Carousel::selectCompleted);
connect(m_serviceMgr, &Service::Manager::previewCompleted, this, &Carousel::previewCompleted);
connect(m_serviceMgr, &Service::Manager::restoreCompleted, this, &Carousel::restoreCompleted);
connect(m_serviceMgr, &Service::Manager::cancelCompleted, this, &Carousel::cancelCompleted);
// Preview on imageid change
connect(this, &Carousel::currentImageIdChanged, this, [this]() {
if (!m_currentImageId.isEmpty()) {
m_serviceMgr->previewWallpaper(m_currentImageId);
}
});
// Update color when imageid changes
connect(this, &Carousel::currentImageIdChanged, this, [this]() {
if (!m_currentImageId.isEmpty()) {
m_paletteMgr->updateColor(m_currentImageId);
}
});
// Update color when selected color changes
connect(this, &Carousel::selectedColorChanged, this, [this]() {
if (!m_currentImageId.isEmpty()) {
m_paletteMgr->updateColor(m_currentImageId);
}
});
// Quit on selected
if (m_configMgr->getActionConfig().quitOnSelected) {
QObject::connect(
this,
&Provider::Carousel::selectCompleted,
app,
&QApplication::quit,
Qt::QueuedConnection);
}
// Quit on cancel
QObject::connect(
this,
&Provider::Carousel::cancelCompleted,
app,
&QApplication::quit,
Qt::QueuedConnection);
// Restore on quit
if (m_configMgr->getActionConfig().restoreOnClose) {
QObject::connect(
app,
&QApplication::aboutToQuit,
m_serviceMgr,
&Service::Manager::restoreOnQuit);
}
// Initial value of sort method
setSortType(m_configMgr->getSortConfig().type);
setSortDescending(m_configMgr->getSortConfig().descending);
}
void start() {
m_configMgr->captureState();
m_imageMgr->loadAndProcess(m_configMgr->getWallpapers());
}
private:
Config::Manager* m_configMgr;
Image::Manager* m_imageMgr;
Palette::Manager* m_paletteMgr;
Service::Manager* m_serviceMgr;
};
} // namespace WallReel::Core::Provider
#endif // WALLREEL_PROVIDER_CAROUSEL_HPP
+32 -24
View File
@@ -5,7 +5,7 @@
#include <QTimer> #include <QTimer>
#include "Config/data.hpp" #include "Config/data.hpp"
#include "Image/model.hpp" #include "Image/manager.hpp"
#include "Palette/manager.hpp" #include "Palette/manager.hpp"
#include "Service/wallpaper.hpp" #include "Service/wallpaper.hpp"
#include "logger.hpp" #include "logger.hpp"
@@ -20,16 +20,11 @@ class Manager : public QObject {
public: public:
Manager( Manager(
const Config::ActionConfigItems& actionConfig, const Config::ActionConfigItems& actionConfig,
Image::Model& imageModel, Image::Manager& imageManager,
Palette::Manager& paletteManager, Palette::Manager& paletteManager,
QObject* parent = nullptr) : m_actionConfig(actionConfig), m_imageModel(imageModel), m_paletteManager(paletteManager) { QObject* parent = nullptr) : m_actionConfig(actionConfig), m_imageManager(imageManager), m_paletteManager(paletteManager) {
m_wallpaperService = new WallpaperService(m_actionConfig, m_paletteManager, this); m_wallpaperService = new WallpaperService(m_actionConfig, m_paletteManager, this);
// Listen on image change
connect(&m_imageModel, &Image::Model::focusedImageChanged, this, &Manager::previewWallpaper);
// Listen on palette change
connect(&m_paletteManager, &Palette::Manager::colorChanged, this, &Manager::previewWallpaper);
connect(&m_paletteManager, &Palette::Manager::colorNameChanged, this, &Manager::previewWallpaper);
// Forward signals // Forward signals
// Direct signal 2 signal connection // Direct signal 2 signal connection
connect(m_wallpaperService, &WallpaperService::previewCompleted, this, &Manager::previewCompleted); connect(m_wallpaperService, &WallpaperService::previewCompleted, this, &Manager::previewCompleted);
@@ -38,26 +33,32 @@ class Manager : public QObject {
connect(m_wallpaperService, &WallpaperService::restoreCompleted, this, &Manager::_onRestoreCompleted); connect(m_wallpaperService, &WallpaperService::restoreCompleted, this, &Manager::_onRestoreCompleted);
} }
Q_INVOKABLE void selectWallpaper(int index) { bool isProcessing() const { return m_isProcessing; }
Logger::debug("ServiceManager", QString("Select wallpaper at index %1").arg(index));
bool hasSelected() const { return m_hasSelected; }
public slots:
void selectWallpaper(const QString& id) {
Logger::debug("ServiceManager", QString("Select wallpaper with id %1").arg(id));
if (m_isProcessing) { if (m_isProcessing) {
Logger::debug("ServiceManager", "Already processing an select action, ignoring new request"); Logger::debug("ServiceManager", "Already processing an select action, ignoring new request");
return; return;
} }
m_isProcessing = true; m_isProcessing = true;
emit isProcessingChanged(); emit isProcessingChanged();
const auto* data = m_imageModel.imageAt(index); const auto* data = m_imageManager.imageAt(id);
if (data) { if (data) {
m_wallpaperService->select(*data); m_wallpaperService->select(*data);
} else { } else {
Logger::warn("ServiceManager", QString("No image data at index %1. Skipping select action.").arg(index)); Logger::warn("ServiceManager", QString("No image data at id %1. Skipping select action.").arg(id));
m_isProcessing = false; m_isProcessing = false;
emit isProcessingChanged(); emit isProcessingChanged();
emit selectCompleted(); emit selectCompleted();
} }
} }
Q_INVOKABLE void restore() { void restore() {
Logger::debug("ServiceManager", "Restore states"); Logger::debug("ServiceManager", "Restore states");
if (m_isProcessing) { if (m_isProcessing) {
Logger::debug("ServiceManager", "Already processing an restore action, ignoring new request"); Logger::debug("ServiceManager", "Already processing an restore action, ignoring new request");
@@ -68,29 +69,36 @@ class Manager : public QObject {
m_wallpaperService->restore(); m_wallpaperService->restore();
} }
Q_INVOKABLE void cancel() { void cancel() {
Logger::debug("ServiceManager", "Cancel action"); Logger::debug("ServiceManager", "Cancel action");
m_wallpaperService->stopAll(); m_wallpaperService->stopAll();
emit cancelCompleted(); emit cancelCompleted();
} }
bool isProcessing() const { return m_isProcessing; } void previewWallpaper(const QString& id) {
bool hasSelected() const { return m_hasSelected; }
public slots:
void previewWallpaper() {
Logger::debug("ServiceManager", "Preview wallpaper"); Logger::debug("ServiceManager", "Preview wallpaper");
const auto* data = m_imageModel.focusedImage(); const auto* data = m_imageManager.imageAt(id);
if (data) { if (data) {
m_wallpaperService->preview(*data); m_wallpaperService->preview(*data);
} else { } else {
Logger::warn("ServiceManager", "No focused image data. Skipping preview action."); Logger::warn("ServiceManager", "No image data at id " + id + ". Skipping preview action.");
emit previewCompleted(); emit previewCompleted();
} }
} }
void restoreOnQuit() {
if (m_hasSelected) {
Logger::debug("ServiceManager", "Quit with selected wallpaper, no need to restore");
return;
}
Logger::debug("ServiceManager", "Restore on quit");
m_wallpaperService->stopAll();
QEventLoop loop;
connect(m_wallpaperService, &WallpaperService::restoreCompleted, &loop, &QEventLoop::quit);
m_wallpaperService->restore();
loop.exec();
}
private slots: private slots:
void _onSelectCompleted() { void _onSelectCompleted() {
@@ -121,7 +129,7 @@ class Manager : public QObject {
private: private:
WallpaperService* m_wallpaperService; WallpaperService* m_wallpaperService;
const Config::ActionConfigItems& m_actionConfig; const Config::ActionConfigItems& m_actionConfig;
Image::Model& m_imageModel; Image::Manager& m_imageManager;
Palette::Manager& m_paletteManager; Palette::Manager& m_paletteManager;
bool m_isProcessing = false; bool m_isProcessing = false;
+2 -2
View File
@@ -108,9 +108,9 @@ QHash<QString, QString> WallReel::Core::Service::WallpaperService::_generateVari
{"width", QString::number(imageData.getTargetSize().width())}, {"width", QString::number(imageData.getTargetSize().width())},
{"height", QString::number(imageData.getTargetSize().height())}, {"height", QString::number(imageData.getTargetSize().height())},
{"palette", palette}, {"palette", palette},
{"color", color}, {"colorName", color},
{"colorHex", hex}, {"colorHex", hex},
{"domColor", m_paletteManager.color().name()}, {"domColorHex", imageData.getDominantColor().name()},
}; };
} }
-1
View File
@@ -6,7 +6,6 @@ qt_add_qml_module(${UILIB_NAME}
Main.qml Main.qml
Pages/LoadingScreen.qml Pages/LoadingScreen.qml
Pages/CarouselScreen.qml Pages/CarouselScreen.qml
Providers/CarouselProvider.qml
Modules/Carousel.qml Modules/Carousel.qml
Modules/TitleBar.qml Modules/TitleBar.qml
Modules/SortControl.qml Modules/SortControl.qml
+6 -6
View File
@@ -4,8 +4,8 @@ import WallReel.Core
import WallReel.UI.Pages import WallReel.UI.Pages
ApplicationWindow { ApplicationWindow {
width: Config.windowWidth width: CarouselProvider.windowWidth
height: Config.windowHeight height: CarouselProvider.windowHeight
// minimumWidth: width // minimumWidth: width
// maximumWidth: width // maximumWidth: width
// minimumHeight: height // minimumHeight: height
@@ -14,15 +14,15 @@ ApplicationWindow {
title: qsTr("WallReel") title: qsTr("WallReel")
LoadingScreen { LoadingScreen {
visible: ImageModel.isLoading visible: CarouselProvider.isLoading
anchors.fill: parent anchors.fill: parent
currentValue: ImageModel.processedCount currentValue: CarouselProvider.processedCount
totalValue: ImageModel.totalCount totalValue: CarouselProvider.totalCount
} }
Loader { Loader {
anchors.fill: parent anchors.fill: parent
active: !ImageModel.isLoading active: !CarouselProvider.isLoading
sourceComponent: CarouselScreen { sourceComponent: CarouselScreen {
} }
+5 -1
View File
@@ -12,11 +12,12 @@ Item {
property int spacing: 0 property int spacing: 0
property int animDuration: 200 property int animDuration: 200
property int count: view.count property int count: view.count
property string currentImageId: view.currentItem ? view.currentItem.imgId : ""
property string currentImageName: view.currentItem ? view.currentItem.imgName : ""
ListView { ListView {
id: view id: view
model: root.model
anchors.fill: parent anchors.fill: parent
orientation: ListView.Horizontal orientation: ListView.Horizontal
spacing: root.spacing spacing: root.spacing
@@ -36,6 +37,7 @@ Item {
view.currentIndex = root.currentIndex; view.currentIndex = root.currentIndex;
view.forceActiveFocus(); view.forceActiveFocus();
} }
model: root.model
Connections { Connections {
function onCurrentIndexChanged() { function onCurrentIndexChanged() {
@@ -51,6 +53,8 @@ Item {
id: delegateItem id: delegateItem
property bool isFocused: ListView.isCurrentItem property bool isFocused: ListView.isCurrentItem
property string imgName: model.imgName
property string imgId: model.imgId
width: isFocused ? root.focusedItemWidth : root.itemWidth width: isFocused ? root.focusedItemWidth : root.itemWidth
height: view.height height: view.height
+4 -4
View File
@@ -38,11 +38,11 @@ Item {
implicitWidth: 200 implicitWidth: 200
displayText: currentIndex < 0 ? "— palette —" : currentText displayText: currentIndex < 0 ? "— palette —" : currentText
model: root.availablePalettes.map((p) => { model: ["(None)"].concat(root.availablePalettes.map((p) => {
return p.name; return p.name;
}) }))
onActivated: (idx) => { onActivated: (idx) => {
root.paletteSelected(idx >= 0 ? root.availablePalettes[idx] : null); root.paletteSelected(idx >= 0 ? root.availablePalettes[idx - 1] : null);
} }
Binding { Binding {
@@ -50,7 +50,7 @@ Item {
property: "currentIndex" property: "currentIndex"
value: root.selectedPalette ? root.availablePalettes.findIndex((p) => { value: root.selectedPalette ? root.availablePalettes.findIndex((p) => {
return p.name === root.selectedPalette.name; return p.name === root.selectedPalette.name;
}) : -1 }) + 1 : 0
} }
} }
+5 -5
View File
@@ -7,10 +7,10 @@ Item {
property var availableSortTypes: [] property var availableSortTypes: []
property string selectedSortType: "" property string selectedSortType: ""
property bool isReverse: false property bool isDescending: false
signal sortTypeSelected(string sortType) signal sortTypeSelected(string sortType)
signal isReverseToggled(bool reverse) signal isDescendingToggled(bool descending)
implicitWidth: row.implicitWidth implicitWidth: row.implicitWidth
implicitHeight: row.implicitHeight implicitHeight: row.implicitHeight
@@ -44,14 +44,14 @@ Item {
} }
ToolButton { ToolButton {
icon.name: root.isReverse ? "view-sort-descending" : "view-sort-ascending" icon.name: root.isDescending ? "view-sort-descending" : "view-sort-ascending"
icon.width: 16 icon.width: 16
icon.height: 16 icon.height: 16
focusPolicy: Qt.NoFocus focusPolicy: Qt.NoFocus
onClicked: root.isReverseToggled(!root.isReverse) onClicked: root.isDescendingToggled(!root.isDescending)
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.delay: 600 ToolTip.delay: 600
ToolTip.text: root.isReverse ? "Descending order" : "Ascending order" ToolTip.text: root.isDescending ? "Descending order" : "Ascending order"
} }
} }
+4 -4
View File
@@ -13,10 +13,10 @@ Item {
readonly property string searchText: searchBar.typedText readonly property string searchText: searchBar.typedText
property alias availableSortTypes: sortCtrl.availableSortTypes property alias availableSortTypes: sortCtrl.availableSortTypes
property alias selectedSortType: sortCtrl.selectedSortType property alias selectedSortType: sortCtrl.selectedSortType
property alias isSortReverse: sortCtrl.isReverse property alias isSortDescending: sortCtrl.isDescending
signal sortTypeSelected(string sortType) signal sortTypeSelected(string sortType)
signal sortReverseToggled(bool reverse) signal sortDescendingToggled(bool descending)
signal searchDismissed() signal searchDismissed()
function requestSearchFocus() { function requestSearchFocus() {
@@ -58,8 +58,8 @@ Item {
onSortTypeSelected: (t) => { onSortTypeSelected: (t) => {
return root.sortTypeSelected(t); return root.sortTypeSelected(t);
} }
onIsReverseToggled: (r) => { onIsDescendingToggled: (r) => {
return root.sortReverseToggled(r); return root.sortDescendingToggled(r);
} }
} }
+48 -62
View File
@@ -1,8 +1,8 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import WallReel.Core
import WallReel.UI.Modules import WallReel.UI.Modules
import WallReel.UI.Providers
Item { Item {
id: root id: root
@@ -20,9 +20,9 @@ Item {
carousel.currentIndex++; carousel.currentIndex++;
} else if (e.key === Qt.Key_Return || e.key === Qt.Key_Enter) } else if (e.key === Qt.Key_Return || e.key === Qt.Key_Enter)
provider.confirm(); CarouselProvider.confirm();
else if (e.key === Qt.Key_Escape) else if (e.key === Qt.Key_Escape)
provider.cancel(); CarouselProvider.cancel();
else else
e.accepted = false; e.accepted = false;
} }
@@ -35,21 +35,6 @@ Item {
target: topBar target: topBar
} }
CarouselProvider {
id: provider
}
// ViewModel → Carousel sync (assignment, not binding, to avoid breakage)
Connections {
function onCurrentIndexChanged() {
if (carousel.currentIndex !== provider.currentIndex)
carousel.currentIndex = provider.currentIndex;
}
target: provider
}
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 12 anchors.margins: 12
@@ -60,29 +45,29 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
totalCount: carousel.count totalCount: carousel.count
title: provider.focusedName title: carousel.currentImageName
availableSortTypes: provider.availableSortTypes availableSortTypes: CarouselProvider.availableSortTypes
isSortReverse: provider.isSortReverse isSortDescending: CarouselProvider.sortDescending
onSortTypeSelected: (t) => { onSortTypeSelected: (t) => {
return provider.setSortType(t); return CarouselProvider.setSortType(t);
} }
onSortReverseToggled: (r) => { onSortDescendingToggled: (r) => {
return provider.setSortReverse(r); return CarouselProvider.setSortDescending(r);
} }
onSearchTextChanged: () => { onSearchTextChanged: () => {
return provider.setSearchText(topBar.searchText); return CarouselProvider.setSearchText(searchText);
} }
Binding { Binding {
target: topBar target: topBar
property: "currentIndex" property: "currentIndex"
value: provider.currentIndex value: carousel.currentIndex
} }
Binding { Binding {
target: topBar target: topBar
property: "selectedSortType" property: "selectedSortType"
value: provider.selectedSortType value: CarouselProvider.sortType
} }
} }
@@ -92,33 +77,22 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: provider.imageModel model: CarouselProvider.imageModel
itemWidth: provider.imageWidth itemWidth: CarouselProvider.imageWidth
itemHeight: provider.imageHeight itemHeight: CarouselProvider.imageHeight
focusedItemWidth: provider.imageWidth * provider.imageFocusScale focusedItemWidth: CarouselProvider.imageWidth * CarouselProvider.imageFocusScale
focusedItemHeight: provider.imageHeight * provider.imageFocusScale focusedItemHeight: CarouselProvider.imageHeight * CarouselProvider.imageFocusScale
onCurrentIndexChanged: {
if (provider.currentIndex !== currentIndex)
provider.setCurrentIndex(currentIndex);
}
Component.onCompleted: {
// Sync initial index to provider from Carousel
if (provider.currentIndex !== currentIndex)
provider.setCurrentIndex(currentIndex);
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onWheel: (e) => { onWheel: (e) => {
if (e.angleDelta.y > 0) { if (e.angleDelta.y > 0) {
if (provider.currentIndex > 0) if (carousel.currentIndex > 0)
provider.currentIndex--; carousel.currentIndex--;
} else if (e.angleDelta.y < 0) { } else if (e.angleDelta.y < 0) {
if (provider.currentIndex < carousel.count - 1) if (carousel.currentIndex < carousel.count - 1)
provider.currentIndex++; carousel.currentIndex++;
} }
} }
@@ -141,64 +115,76 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
from: 0 from: 0
to: Math.max(0, carousel.count - 1) to: Math.max(0, carousel.count - 1)
value: provider.currentIndex value: carousel.currentIndex
onMoved: provider.currentIndex = Math.round(value) onMoved: carousel.currentIndex = Math.round(value)
} }
BottomBar { BottomBar {
id: bottomBar id: bottomBar
Layout.fillWidth: true Layout.fillWidth: true
availablePalettes: provider.availablePalettes availablePalettes: CarouselProvider.availablePalettes
availableColors: provider.availableColors availableColors: CarouselProvider.selectedPalette ? CarouselProvider.selectedPalette.colors : []
onPaletteSelected: (p) => { onPaletteSelected: (p) => {
return provider.selectPalette(p); return CarouselProvider.requestSelectPalette(p);
} }
onColorSelected: (c) => { onColorSelected: (c) => {
return provider.selectColor(c); return CarouselProvider.requestSelectColor(c);
} }
onRestoreClicked: provider.restore() onRestoreClicked: CarouselProvider.restore()
onConfirmClicked: provider.confirm() onConfirmClicked: CarouselProvider.confirm()
onCancelClicked: provider.cancel() onCancelClicked: CarouselProvider.cancel()
Binding { Binding {
target: bottomBar target: bottomBar
property: "selectedPalette" property: "selectedPalette"
value: provider.selectedPalette value: CarouselProvider.selectedPalette
} }
Binding { Binding {
target: bottomBar target: bottomBar
property: "selectedColor" property: "selectedColor"
value: provider.selectedColor value: CarouselProvider.selectedColor
} }
Binding { Binding {
target: bottomBar target: bottomBar
property: "colorName" property: "colorName"
value: provider.colorName value: CarouselProvider.colorName
} }
Binding { Binding {
target: bottomBar target: bottomBar
property: "colorHex" property: "colorHex"
value: provider.colorHex value: CarouselProvider.color
} }
Binding { Binding {
target: bottomBar target: bottomBar
property: "colorValue" property: "colorValue"
value: provider.colorValue value: CarouselProvider.color
} }
Binding { Binding {
target: bottomBar target: bottomBar
property: "actionsEnabled" property: "actionsEnabled"
value: !provider.isProcessing value: !CarouselProvider.isProcessing
} }
} }
} }
Connections {
function onCurrentImageIdChanged() {
CarouselProvider.setCurrentImageId(carousel.currentImageId);
}
function onCurrentIndexChanged() {
CarouselProvider.setCurrentIndex(carousel.currentIndex);
}
target: carousel
}
} }
@@ -1,79 +0,0 @@
import QtQuick
import WallReel.Core
QtObject {
id: root
//// Image model
readonly property var imageModel: ImageModel
readonly property bool isLoading: ImageModel.isLoading
readonly property int processedCount: ImageModel.processedCount
readonly property int totalCount: ImageModel.totalCount
// Image display dimensions (from Config)
readonly property int imageWidth: Config.imageWidth
readonly property int imageHeight: Config.imageHeight
readonly property real imageFocusScale: Config.imageFocusScale
// Carousel selection state
readonly property int currentIndex: ImageModel.currentIndex
// Image name
readonly property string focusedName: ImageModel.focusedName
//// Sort
readonly property var availableSortTypes: ["None", "Name", "Date", "Size"]
readonly property string selectedSortType: ImageModel.currentSortType
readonly property bool isSortReverse: ImageModel.currentSortReverse
//// Palette / Color
readonly property var availablePalettes: PaletteManager.availablePalettes
readonly property var selectedPalette: PaletteManager.selectedPalette // PaletteItem | null
readonly property var availableColors: selectedPalette ? selectedPalette.colors : []
readonly property var selectedColor: PaletteManager.selectedColor // ColorItem | null (null means "auto")
readonly property string colorName: PaletteManager.colorName
readonly property string colorHex: PaletteManager.color
readonly property color colorValue: PaletteManager.color
//// Actions state
readonly property bool isProcessing: ServiceManager.isProcessing
// Actions
function confirm() {
ServiceManager.selectWallpaper(currentIndex);
}
function restore() {
ServiceManager.restore();
}
function cancel() {
ServiceManager.cancel();
}
function setCurrentIndex(index) {
ImageModel.currentIndex = index;
}
function focusSearch() {
searchBar.requestFocus();
}
function setSortType(type) {
ImageModel.currentSortType = type;
}
function setSortReverse(reverse) {
ImageModel.currentSortReverse = reverse;
}
function selectPalette(palette) {
PaletteManager.selectedPalette = palette;
}
function selectColor(colorItem) {
PaletteManager.selectedColor = colorItem;
}
function setSearchText(text) {
ImageModel.setSearchText(text);
if (currentIndex != 0)
currentIndex = 0;
}
}
+18 -82
View File
@@ -9,13 +9,8 @@
Q_IMPORT_QML_PLUGIN(WallReel_CorePlugin) Q_IMPORT_QML_PLUGIN(WallReel_CorePlugin)
Q_IMPORT_QML_PLUGIN(WallReel_UIPlugin) Q_IMPORT_QML_PLUGIN(WallReel_UIPlugin)
#include "Cache/manager.hpp" #include "Core/Provider/bootstrap.hpp"
#include "Core/Config/manager.hpp" #include "Core/Provider/carousel.hpp"
#include "Core/Image/model.hpp"
#include "Core/Palette/data.hpp"
#include "Core/Palette/manager.hpp"
#include "Core/Service/manager.hpp"
#include "Core/Utils/misc.hpp"
#include "Core/appoptions.hpp" #include "Core/appoptions.hpp"
#include "Core/logger.hpp" #include "Core/logger.hpp"
#include "version.h" #include "version.h"
@@ -25,97 +20,37 @@ using namespace WallReel::Core;
WALLREEL_DECLARE_SENDER("Main") WALLREEL_DECLARE_SENDER("Main")
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
AppOptions s_options;
QApplication a(argc, argv); QApplication a(argc, argv);
a.setApplicationName(APP_NAME); a.setApplicationName(APP_NAME);
a.setApplicationVersion(APP_VERSION); a.setApplicationVersion(APP_VERSION);
a.setWindowIcon(QIcon(QString(":/%1.svg").arg(APP_NAME))); a.setWindowIcon(QIcon(QString(":/%1.svg").arg(APP_NAME)));
{
Logger::init(); Logger::init();
s_options.parseArgs(a);
if (s_options.doReturn) { AppOptions options;
return s_options.errorText.isEmpty() ? 0 : 1; options.parseArgs(a);
if (options.doReturn) {
return options.errorText.isEmpty() ? 0 : 1;
} }
QQmlApplicationEngine engine; Provider::Bootstrap bootstrap(options);
auto cacheMgr = new Cache::Manager(Utils::getCacheDir()); if (options.clearCache) {
if (s_options.clearCache) {
cacheMgr->clearCache();
return 0; return 0;
} }
auto config = new Config::Manager( Provider::Carousel provider(&a, bootstrap);
Utils::getConfigDir(),
s_options.appendDirs,
s_options.configPath,
&engine);
qmlRegisterSingletonInstance( qmlRegisterSingletonInstance(
COREMODULE_URI, COREMODULE_URI,
MODULE_VERSION_MAJOR, MODULE_VERSION_MAJOR,
MODULE_VERSION_MINOR, MODULE_VERSION_MINOR,
"Config", "CarouselProvider",
config); &provider);
{
auto imageModel = new Image::Model( QQmlApplicationEngine engine;
config->getSortConfig(),
*cacheMgr,
config->getFocusImageSize(),
config);
qmlRegisterSingletonInstance(
COREMODULE_URI,
MODULE_VERSION_MAJOR,
MODULE_VERSION_MINOR,
"ImageModel",
imageModel);
auto paletteMgr = new Palette::Manager(
config->getThemeConfig(),
*imageModel,
imageModel);
engine.rootContext()->setContextProperty("PaletteManager", paletteMgr);
qRegisterMetaType<Palette::PaletteItem>("PaletteItem");
qRegisterMetaType<Palette::ColorItem>("ColorItem");
auto Service = new Service::Manager(
config->getActionConfig(),
*imageModel,
*paletteMgr,
paletteMgr);
qmlRegisterSingletonInstance(
COREMODULE_URI,
MODULE_VERSION_MAJOR,
MODULE_VERSION_MINOR,
"ServiceManager",
Service);
if (config->getActionConfig().quitOnSelected) {
QObject::connect(
Service,
&Service::Manager::selectCompleted,
&a,
&QApplication::quit,
Qt::QueuedConnection);
}
QObject::connect(
Service,
&Service::Manager::cancelCompleted,
&a,
&QApplication::quit,
Qt::QueuedConnection);
if (config->getActionConfig().restoreOnClose) {
QObject::connect(
&a,
&QApplication::aboutToQuit,
Service,
[Service]() {
if (!Service->hasSelected()) {
Service->restore();
}
});
}
QObject::connect( QObject::connect(
&engine, &engine,
@@ -125,8 +60,9 @@ int main(int argc, char* argv[]) {
Qt::QueuedConnection); Qt::QueuedConnection);
engine.loadFromModule(UIMODULE_URI, "Main"); engine.loadFromModule(UIMODULE_URI, "Main");
config->captureState(); provider.start();
imageModel->loadAndProcess(config->getWallpapers());
return a.exec(); return a.exec();
}
}
} }