🚧 wip: ♻️ refactor: chekkupointo

This commit is contained in:
2026-02-17 23:25:35 +01:00
parent 9622c5b1fe
commit fff2e56467
27 changed files with 494 additions and 451 deletions
-4
View File
@@ -64,10 +64,6 @@ set_target_properties(${EXECUTABLE_NAME} PROPERTIES
)
target_link_libraries(${EXECUTABLE_NAME} PRIVATE
Qt6::Quick
Qt6::Widgets
Qt6::QuickControls2
Qt6::Concurrent
${CORELIB_NAME}
${UILIB_NAME}
)
+3 -18
View File
@@ -1,7 +1,7 @@
project(Tests LANGUAGES CXX)
find_package(Qt6 REQUIRED COMPONENTS Core Test)
find_package(Qt6 REQUIRED COMPONENTS Test)
add_executable(tst_configmgr
tst_configmgr.cpp
@@ -15,26 +15,11 @@ add_test(NAME tst_configmgr COMMAND tst_configmgr)
add_test(NAME tst_imagemodel COMMAND tst_imagemodel)
target_link_libraries(tst_configmgr PRIVATE
Qt6::Core
Qt6::Test
Qt6::Gui
wallreel-core
${CORELIB_NAME}
)
target_link_libraries(tst_imagemodel PRIVATE
Qt6::Core
Qt6::Test
Qt6::Gui
Qt6::Quick
wallreel-core
)
target_include_directories(tst_configmgr PRIVATE
${CMAKE_SOURCE_DIR}/WallReel/Core
${CMAKE_BINARY_DIR}/generated
)
target_include_directories(tst_imagemodel PRIVATE
${CMAKE_SOURCE_DIR}/WallReel/Core
${CMAKE_BINARY_DIR}/generated
${CORELIB_NAME}
)
+17 -15
View File
@@ -8,7 +8,9 @@
#include <QTemporaryDir>
#include <QTest>
#include "configmgr.hpp"
#include "Config/manager.hpp"
using namespace WallReel::Core;
class TestConfigMgr : public QObject {
Q_OBJECT
@@ -67,7 +69,7 @@ void TestConfigMgr::testDefaults() {
// Empty config file
writeConfig(QJsonObject());
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
// Check Style Defaults
QCOMPARE(config.getImageWidth(), 320);
@@ -144,7 +146,7 @@ void TestConfigMgr::testFullConfigParsing() {
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
// Assertions
QCOMPARE(config.getWallpaperConfig().dirs.size(), 1);
@@ -184,7 +186,7 @@ void TestConfigMgr::testInvalidConfigValues() {
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
// Should retain defaults
QCOMPARE(config.getImageWidth(), 320);
@@ -208,7 +210,7 @@ void TestConfigMgr::testWallpaperScanRecursive() {
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 2);
@@ -235,7 +237,7 @@ void TestConfigMgr::testWallpaperScanNonRecursive() {
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 1);
@@ -262,7 +264,7 @@ void TestConfigMgr::testWallpaperExcludes() {
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 1);
@@ -281,7 +283,7 @@ void TestConfigMgr::testExplicitPaths() {
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 1);
@@ -308,7 +310,7 @@ void TestConfigMgr::testImageExtensions() {
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
@@ -332,7 +334,7 @@ void TestConfigMgr::testSortTypes() {
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::None);
QCOMPARE(config.getSortConfig().reverse, false);
}
@@ -344,7 +346,7 @@ void TestConfigMgr::testSortTypes() {
sortObj["reverse"] = true;
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Name);
QCOMPARE(config.getSortConfig().reverse, true);
}
@@ -355,7 +357,7 @@ void TestConfigMgr::testSortTypes() {
sortObj["type"] = "size";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Size);
}
// 4. Date sort
@@ -365,7 +367,7 @@ void TestConfigMgr::testSortTypes() {
sortObj["type"] = "date";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Date);
}
// 5. Invalid sort -> fallback to default (Name)
@@ -375,7 +377,7 @@ void TestConfigMgr::testSortTypes() {
sortObj["type"] = "invalid_blah";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
// Default initialized in Config constructor is Name
// But warning is logged
QCOMPARE(config.getSortConfig().type, Config::SortType::Name);
@@ -387,7 +389,7 @@ void TestConfigMgr::testSortTypes() {
sortObj["type"] = "DaTe";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
Config::Manager config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Date);
}
}
+32 -30
View File
@@ -3,9 +3,11 @@
#include <QTemporaryDir>
#include <QtTest>
#include "configmgr.hpp"
#include "imagemodel.hpp"
#include "imageprovider.hpp"
#include "Config/manager.hpp"
#include "Image/model.hpp"
#include "Image/provider.hpp"
using namespace WallReel::Core;
class TestImageModel : public QObject {
Q_OBJECT
@@ -23,7 +25,7 @@ class TestImageModel : public QObject {
QString m_pathC;
void createTestFiles();
void waitForModel(ImageModel* model);
void waitForModel(Image::Model* model);
};
// clang-format off
@@ -115,11 +117,11 @@ void TestImageModel::createTestFiles() {
}
}
void TestImageModel::waitForModel(ImageModel* model) {
void TestImageModel::waitForModel(Image::Model* model) {
if (!model->isLoading()) {
return;
}
QSignalSpy spy(model, &ImageModel::isLoadingChanged);
QSignalSpy spy(model, &Image::Model::isLoadingChanged);
while (model->isLoading()) {
if (!spy.wait(5000)) {
qWarning() << "Timeout waiting for model to load";
@@ -133,8 +135,8 @@ void TestImageModel::testSortName() {
sortConfig.type = Config::SortType::Name;
sortConfig.reverse = false;
ImageProvider provider;
ImageModel model(provider, sortConfig, QSize(100, 100));
Image::Provider provider;
Image::Model model(provider, sortConfig, QSize(100, 100));
QStringList paths = {m_pathB, m_pathA, m_pathC}; // Unordered input
model.loadAndProcess(paths);
@@ -143,9 +145,9 @@ void TestImageModel::testSortName() {
QCOMPARE(model.rowCount(), 3);
// Expected: a.gif, b.gif, c.gif
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(0), Image::Model::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(1), Image::Model::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(2), Image::Model::NameRole).toString(), "c.gif");
// Reverse
sortConfig.reverse = true;
@@ -154,9 +156,9 @@ void TestImageModel::testSortName() {
QCOMPARE(model.rowCount(), 3);
// Expected: c.gif, b.gif, a.gif
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(0), Image::Model::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), Image::Model::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(2), Image::Model::NameRole).toString(), "a.gif");
}
void TestImageModel::testSortDate() {
@@ -164,8 +166,8 @@ void TestImageModel::testSortDate() {
sortConfig.type = Config::SortType::Date;
sortConfig.reverse = false;
ImageProvider provider;
ImageModel model(provider, sortConfig, QSize(100, 100));
Image::Provider provider;
Image::Model model(provider, sortConfig, QSize(100, 100));
QStringList paths = {m_pathA, m_pathC, m_pathB};
model.loadAndProcess(paths);
@@ -174,17 +176,17 @@ void TestImageModel::testSortDate() {
QCOMPARE(model.rowCount(), 3);
// Expected: c (old), a (mid), b (new)
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(0), Image::Model::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), Image::Model::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), Image::Model::NameRole).toString(), "b.gif");
// Reverse (Newest first)
sortConfig.reverse = true;
model.sortUpdate();
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(0), Image::Model::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(1), Image::Model::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), Image::Model::NameRole).toString(), "c.gif");
}
void TestImageModel::testSortSize() {
@@ -192,8 +194,8 @@ void TestImageModel::testSortSize() {
sortConfig.type = Config::SortType::Size;
sortConfig.reverse = false;
ImageProvider provider;
ImageModel model(provider, sortConfig, QSize(100, 100));
Image::Provider provider;
Image::Model model(provider, sortConfig, QSize(100, 100));
QStringList paths = {m_pathB, m_pathC, m_pathA};
model.loadAndProcess(paths);
@@ -201,17 +203,17 @@ void TestImageModel::testSortSize() {
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(0), Image::Model::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), Image::Model::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), Image::Model::NameRole).toString(), "b.gif");
// Reverse
sortConfig.reverse = true;
model.sortUpdate();
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(0), Image::Model::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(1), Image::Model::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), Image::Model::NameRole).toString(), "c.gif");
}
QTEST_MAIN(TestImageModel)
+11 -10
View File
@@ -2,24 +2,25 @@ qt_add_qml_module(${CORELIB_NAME}
URI ${COREMODULE_URI}
VERSION ${MODULE_VERSION_MAJOR}.${MODULE_VERSION_MINOR}
SOURCES
imagedata.hpp imagedata.cpp
utils/logger.hpp utils/logger.cpp
utils/misc.hpp
configmgr.hpp configmgr.cpp
imagemodel.hpp imagemodel.cpp
imageprovider.hpp imageprovider.cpp
Image/data.hpp Image/data.cpp
Image/model.hpp Image/model.cpp
Image/provider.hpp Image/provider.cpp
Palette/data.hpp
Palette/manager.hpp Palette/manager.cpp
Config/manager.hpp Config/manager.cpp
logger.hpp logger.cpp
wallpaperservice.hpp wallpaperservice.cpp
palette/data.hpp
palette/manager.hpp palette/manager.cpp
appoptions.hpp appoptions.cpp
)
target_link_libraries(${CORELIB_NAME} PRIVATE
target_link_libraries(${CORELIB_NAME} PUBLIC
Qt6::Quick
Qt6::Widgets
Qt6::QuickControls2
Qt6::Concurrent
)
target_include_directories(${CORELIB_NAME} PRIVATE
target_include_directories(${CORELIB_NAME} PUBLIC
${CMAKE_BINARY_DIR}/generated
${CMAKE_CURRENT_LIST_DIR}
)
@@ -1,4 +1,4 @@
#include "configmgr.hpp"
#include "manager.hpp"
#include <QDir>
#include <QFile>
@@ -8,43 +8,40 @@
#include <QProcessEnvironment>
#include <QStandardPaths>
#include "utils/logger.hpp"
#include "utils/misc.hpp"
using namespace GeneralLogger;
#include "Utils/misc.hpp"
#include "logger.hpp"
const QString Config::s_DefaultConfigFileName = "config.json";
Config::Config(
WallReel::Core::Config::Manager::Manager(
const QString& configDir,
const QStringList& searchDirs,
const QString& configPath,
QObject* parent)
: QObject(parent), m_configDir(configDir) {
if (configPath.isEmpty()) {
info(QString("Configuration directory: %1").arg(configDir));
Logger::info(QString("Configuration directory: %1").arg(configDir));
_loadConfig(configDir + QDir::separator() + s_DefaultConfigFileName);
} else {
_loadConfig(configPath);
}
if (!searchDirs.isEmpty()) {
info(QString("Additional search directories: %1").arg(searchDirs.join(", ")));
Logger::info(QString("Additional search directories: %1").arg(searchDirs.join(", ")));
for (const auto& dir : searchDirs) {
m_wallpaperConfig.dirs.append({dir, false});
}
}
debug("Loading wallpapers ...");
Logger::debug("Loading wallpapers ...");
_loadWallpapers();
}
Config::~Config() {
WallReel::Core::Config::Manager::~Manager() {
}
void Config::_loadConfig(const QString& configPath) {
info(QString("Loading configuration from: %1").arg(configPath));
void WallReel::Core::Config::Manager::_loadConfig(const QString& configPath) {
Logger::info(QString("Loading configuration from: %1").arg(configPath));
QFile configFile(configPath);
if (!configFile.open(QIODevice::ReadOnly)) {
critical(QString("Failed to open config file: %1").arg(configPath));
Logger::critical(QString("Failed to open config file: %1").arg(configPath));
return;
}
QByteArray configData = configFile.readAll();
@@ -52,7 +49,7 @@ void Config::_loadConfig(const QString& configPath) {
QJsonDocument jsonDoc = QJsonDocument::fromJson(configData);
if (jsonDoc.isNull() || !jsonDoc.isObject()) {
critical(QString("Invalid JSON format in config file"));
Logger::critical(QString("Invalid JSON format in config file"));
return;
}
@@ -65,7 +62,7 @@ void Config::_loadConfig(const QString& configPath) {
_loadSortConfig(jsonObj);
}
void Config::_loadWallpaperConfig(const QJsonObject& root) {
void WallReel::Core::Config::Manager::_loadWallpaperConfig(const QJsonObject& root) {
if (!root.contains("wallpaper") || !root["wallpaper"].isObject()) {
return;
}
@@ -74,7 +71,7 @@ void Config::_loadWallpaperConfig(const QJsonObject& root) {
if (config.contains("paths") && config["paths"].isArray()) {
for (const auto& item : config["paths"].toArray()) {
if (item.isString()) {
m_wallpaperConfig.paths.append(::expandPath(item.toString()));
m_wallpaperConfig.paths.append(Utils::expandPath(item.toString()));
}
}
}
@@ -85,7 +82,7 @@ void Config::_loadWallpaperConfig(const QJsonObject& root) {
QJsonObject obj = item.toObject();
if (obj.contains("path") && obj["path"].isString()) {
WallpaperConfigItems::WallpaperDirConfigItem dirConfig;
dirConfig.path = ::expandPath(obj["path"].toString());
dirConfig.path = Utils::expandPath(obj["path"].toString());
if (obj.contains("recursive") && obj["recursive"].isBool()) {
dirConfig.recursive = obj["recursive"].toBool();
} else {
@@ -102,7 +99,7 @@ void Config::_loadWallpaperConfig(const QJsonObject& root) {
if (item.isString()) {
auto regex = QRegularExpression(item.toString());
if (!regex.isValid()) {
warn(QString("Invalid regular expression in config: %1").arg(item.toString()));
Logger::warn(QString("Invalid regular expression in config: %1").arg(item.toString()));
} else {
m_wallpaperConfig.excludes.append(regex);
}
@@ -111,7 +108,7 @@ void Config::_loadWallpaperConfig(const QJsonObject& root) {
}
}
void Config::_loadPaletteConfig(const QJsonObject& root) {
void WallReel::Core::Config::Manager::_loadPaletteConfig(const QJsonObject& root) {
if (!root.contains("palettes") || !root["palettes"].isArray()) {
return;
}
@@ -137,7 +134,7 @@ void Config::_loadPaletteConfig(const QJsonObject& root) {
if (color.isValid()) {
colorConfig.value = color;
} else {
warn(QString("Invalid color string in config: %1").arg(colorObj["value"].toString()));
Logger::warn(QString("Invalid color string in config: %1").arg(colorObj["value"].toString()));
}
}
} else if (colorItem.isString()) {
@@ -145,7 +142,7 @@ void Config::_loadPaletteConfig(const QJsonObject& root) {
if (color.isValid()) {
colorConfig.value = color;
} else {
warn(QString("Invalid color string in config: %1").arg(colorItem.toString()));
Logger::warn(QString("Invalid color string in config: %1").arg(colorItem.toString()));
}
}
if (colorConfig.value.isValid()) {
@@ -158,7 +155,7 @@ void Config::_loadPaletteConfig(const QJsonObject& root) {
}
}
void Config::_loadActionConfig(const QJsonObject& root) {
void WallReel::Core::Config::Manager::_loadActionConfig(const QJsonObject& root) {
if (!root.contains("action") || !root["action"].isObject()) {
return;
}
@@ -213,7 +210,7 @@ void Config::_loadActionConfig(const QJsonObject& root) {
}
}
void Config::_loadStyleConfig(const QJsonObject& root) {
void WallReel::Core::Config::Manager::_loadStyleConfig(const QJsonObject& root) {
if (!root.contains("style") || !root["style"].isObject()) {
return;
}
@@ -251,7 +248,7 @@ void Config::_loadStyleConfig(const QJsonObject& root) {
}
}
void Config::_loadSortConfig(const QJsonObject& root) {
void WallReel::Core::Config::Manager::_loadSortConfig(const QJsonObject& root) {
if (!root.contains("sort") || !root["sort"].isObject()) {
return;
}
@@ -270,7 +267,7 @@ void Config::_loadSortConfig(const QJsonObject& root) {
} else if (type == "size") {
m_sortConfig.type = SortType::Size;
} else {
warn(QString("Unknown sort type: %1").arg(type));
Logger::warn(QString("Unknown sort type: %1").arg(type));
}
}
}
@@ -282,30 +279,30 @@ void Config::_loadSortConfig(const QJsonObject& root) {
}
}
void Config::_loadWallpapers() {
void WallReel::Core::Config::Manager::_loadWallpapers() {
m_wallpapers.clear();
QSet<QString> paths;
debug(QString("Loading wallpapers from %1 specified paths...").arg(m_wallpaperConfig.paths.size()));
Logger::debug(QString("Loading wallpapers from %1 specified paths...").arg(m_wallpaperConfig.paths.size()));
for (const QString& path : std::as_const(m_wallpaperConfig.paths)) {
paths.insert(path);
}
debug(QString("Loading wallpapers from %1 specified directories...").arg(m_wallpaperConfig.dirs.size()));
Logger::debug(QString("Loading wallpapers from %1 specified directories...").arg(m_wallpaperConfig.dirs.size()));
for (const auto& dirConfig : std::as_const(m_wallpaperConfig.dirs)) {
if (checkDir(dirConfig.path)) {
if (Utils::checkDir(dirConfig.path)) {
std::function<void(const QDir&)> scanDir;
scanDir = [&](const QDir& d) {
QStringList files = d.entryList(QDir::Files | QDir::NoDotAndDotDot);
for (const QString& file : std::as_const(files)) {
QString filePath = d.filePath(file);
paths.insert(expandPath(filePath));
paths.insert(Utils::expandPath(filePath));
}
if (dirConfig.recursive) {
QStringList subDirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
debug(QString("Scanning directory '%1' for subdirectories... Found %2").arg(d.absolutePath()).arg(subDirs.size()));
Logger::debug(QString("Scanning directory '%1' for subdirectories... Found %2").arg(d.absolutePath()).arg(subDirs.size()));
for (const QString& subDir : std::as_const(subDirs)) {
scanDir(QDir(d.filePath(subDir)));
}
@@ -313,17 +310,17 @@ void Config::_loadWallpapers() {
};
scanDir(QDir(dirConfig.path));
} else {
warn(QString("Directory '%1' does not exist").arg(dirConfig.path));
Logger::warn(QString("Directory '%1' does not exist").arg(dirConfig.path));
}
}
debug(QString("Excluding %1 specified paths...").arg(m_wallpaperConfig.excludes.size()));
Logger::debug(QString("Excluding %1 specified paths...").arg(m_wallpaperConfig.excludes.size()));
QStringList toRemove;
for (const auto& exclude : std::as_const(m_wallpaperConfig.excludes)) {
for (const QString& path : std::as_const(paths)) {
if (exclude.match(path).hasMatch()) {
toRemove.append(path);
debug(QString("Excluded path '%1' matched by regex '%2'").arg(path).arg(exclude.pattern()));
Logger::debug(QString("Excluded path '%1' matched by regex '%2'").arg(path).arg(exclude.pattern()));
}
}
}
@@ -333,12 +330,12 @@ void Config::_loadWallpapers() {
m_wallpapers.reserve(paths.size());
for (const QString& path : paths) {
if (checkImageFile(path)) {
if (Utils::checkImageFile(path)) {
m_wallpapers.append(path);
} else {
warn(QString("File '%1' is not recognized as a valid image file").arg(path));
Logger::warn(QString("File '%1' is not recognized as a valid image file").arg(path));
}
}
info(QString("Found %1 files").arg(paths.size()));
Logger::info(QString("Found %1 files").arg(paths.size()));
}
@@ -1,8 +1,6 @@
#ifndef WALLREEL_CONFIGMGR_HPP
#define WALLREEL_CONFIGMGR_HPP
#include <qregularexpression.h>
#include <QColor>
#include <QObject>
#include <QRegularExpression>
@@ -44,7 +42,67 @@
// date: older before newer
// size: smaller before larger
class Config : public QObject {
namespace WallReel::Core::Config {
static const QString s_DefaultConfigFileName = "config.json";
enum class SortType : int {
None = 0, // "none"
Name, // "name"
Date, // "date"
Size, // "size"
};
struct WallpaperConfigItems {
struct WallpaperDirConfigItem {
QString path;
bool recursive;
};
QStringList paths;
QList<WallpaperDirConfigItem> dirs;
QList<QRegularExpression> excludes;
};
struct PaletteConfigItems {
struct PaletteColorConfigItem {
QString name;
QColor value;
};
struct PaletteConfigItem {
QString name;
QList<PaletteColorConfigItem> colors;
};
QList<PaletteConfigItem> palettes;
};
struct ActionConfigItems {
QHash<QString, QString> saveState;
QString onSelected;
QString onPreview;
QString onRestore;
int previewDebounceTime = 300; // milliseconds
bool printSelected = false;
bool printPreview = false;
};
struct StyleConfigItems {
double imageFocusScale = 1.5;
int imageWidth = 320;
int imageHeight = 200;
int windowWidth = 750;
int windowHeight = 500;
};
struct SortConfigItems {
SortType type = SortType::Name;
bool reverse = false;
};
class Manager : public QObject {
Q_OBJECT
Q_PROPERTY(int imageWidth READ getImageWidth CONSTANT)
@@ -54,69 +112,13 @@ class Config : public QObject {
Q_PROPERTY(int windowHeight READ getWindowHeight CONSTANT)
public:
enum class SortType : int {
None = 0, // "none"
Name, // "name"
Date, // "date"
Size, // "size"
};
struct WallpaperConfigItems {
struct WallpaperDirConfigItem {
QString path;
bool recursive;
};
QStringList paths;
QList<WallpaperDirConfigItem> dirs;
QList<QRegularExpression> excludes;
};
struct PaletteConfigItems {
struct PaletteColorConfigItem {
QString name;
QColor value;
};
struct PaletteConfigItem {
QString name;
QList<PaletteColorConfigItem> colors;
};
QList<PaletteConfigItem> palettes;
};
struct ActionConfigItems {
QHash<QString, QString> saveState;
QString onSelected;
QString onPreview;
QString onRestore;
int previewDebounceTime = 300; // milliseconds
bool printSelected = false;
bool printPreview = false;
};
struct StyleConfigItems {
double imageFocusScale = 1.5;
int imageWidth = 320;
int imageHeight = 200;
int windowWidth = 750;
int windowHeight = 500;
};
struct SortConfigItems {
SortType type = SortType::Name;
bool reverse = false;
};
Config(
Manager(
const QString& configDir,
const QStringList& searchDirs = {},
const QString& configPath = "", // Override the default config path
QObject* parent = nullptr);
~Config();
~Manager();
const QStringList& getWallpapers() const { return m_wallpapers; }
@@ -146,7 +148,6 @@ class Config : public QObject {
return QSize{m_styleConfig.imageWidth, m_styleConfig.imageHeight} * m_styleConfig.imageFocusScale;
}
static const QString s_DefaultConfigFileName;
const QString m_configDir;
private:
@@ -168,4 +169,6 @@ class Config : public QObject {
QStringList m_wallpapers;
};
} // namespace WallReel::Core::Config
#endif // WALLREEL_CONFIGMGR_HPP
@@ -1,13 +1,11 @@
#include "imagedata.hpp"
#include "data.hpp"
#include <QImageReader>
#include "utils/logger.hpp"
#include "../logger.hpp"
using namespace GeneralLogger;
ImageData* ImageData::create(const QString& path, const QSize& size) {
ImageData* ret = new ImageData(path, size);
WallReel::Core::Image::Data* WallReel::Core::Image::Data::create(const QString& path, const QSize& size) {
Data* ret = new Data(path, size);
if (!ret->isValid()) {
delete ret;
return nullptr;
@@ -15,11 +13,11 @@ ImageData* ImageData::create(const QString& path, const QSize& size) {
return ret;
}
ImageData::ImageData(const QString& path, const QSize& targetSize)
WallReel::Core::Image::Data::Data(const QString& path, const QSize& targetSize)
: m_file(path) {
QImageReader reader(path);
if (!reader.canRead()) {
warn(QString("Failed to load image from path: %1").arg(path));
Logger::warn(QString("Failed to load image from path: %1").arg(path));
return;
}
@@ -39,7 +37,7 @@ ImageData::ImageData(const QString& path, const QSize& targetSize)
}
if (!reader.read(&m_image)) {
warn(QString("Failed to load image from path: %1").arg(path));
Logger::warn(QString("Failed to load image from path: %1").arg(path));
return;
}
@@ -4,15 +4,17 @@
#include <QFileInfo>
#include <QImage>
class ImageData {
namespace WallReel::Core::Image {
class Data {
QString m_id;
QFileInfo m_file;
QImage m_image;
ImageData(const QString& path, const QSize& size);
Data(const QString& path, const QSize& size);
public:
static ImageData* create(const QString& path, const QSize& size);
static Data* create(const QString& path, const QSize& size);
const QImage& getImage() const { return m_image; }
@@ -33,4 +35,6 @@ class ImageData {
private:
};
} // namespace WallReel::Core::Image
#endif // WALLREEL_IMAGEDATA_HPP
@@ -1,13 +1,12 @@
#include "imagemodel.hpp"
#include "model.hpp"
#include <QFuture>
#include <QtConcurrent>
#include "imagedata.hpp"
#include "data.hpp"
ImageModel::ImageModel(
ImageProvider& provider,
WallReel::Core::Image::Model::Model(
Provider& provider,
const Config::SortConfigItems& sortConfig,
QSize thumbnailSize,
QObject* parent)
@@ -17,9 +16,9 @@ ImageModel::ImageModel(
m_thumbnailSize(thumbnailSize) {
connect(
&m_watcher,
&QFutureWatcher<ImageData*>::finished,
&QFutureWatcher<Data*>::finished,
this,
&ImageModel::_onProcessingFinished);
&Model::_onProcessingFinished);
connect(
&m_progressUpdateTimer,
&QTimer::timeout,
@@ -29,21 +28,21 @@ ImageModel::ImageModel(
});
}
ImageModel::~ImageModel() {
WallReel::Core::Image::Model::~Model() {
m_watcher.cancel();
m_watcher.waitForFinished();
qDeleteAll(m_data);
m_data.clear();
}
int ImageModel::rowCount(const QModelIndex& parent) const {
int WallReel::Core::Image::Model::rowCount(const QModelIndex& parent) const {
if (parent.isValid()) {
return 0;
}
return m_data.count();
}
QVariant ImageModel::data(const QModelIndex& index, int role) const {
QVariant WallReel::Core::Image::Model::data(const QModelIndex& index, int role) const {
if (!index.isValid() || index.row() >= m_data.count()) {
return QVariant();
}
@@ -61,7 +60,7 @@ QVariant ImageModel::data(const QModelIndex& index, int role) const {
}
}
void ImageModel::loadAndProcess(const QStringList& paths) {
void WallReel::Core::Image::Model::loadAndProcess(const QStringList& paths) {
if (m_isLoading) {
return;
}
@@ -72,10 +71,10 @@ void ImageModel::loadAndProcess(const QStringList& paths) {
m_processedCount = 0;
m_progressUpdateTimer.start(s_ProgressUpdateIntervalMs);
const auto thumbnailSize = m_thumbnailSize;
const auto counterPtr = &m_processedCount;
QFuture<ImageData*> future = QtConcurrent::mapped(paths, [thumbnailSize, counterPtr](const QString& path) {
auto data = ImageData::create(path, thumbnailSize);
const auto thumbnailSize = m_thumbnailSize;
const auto counterPtr = &m_processedCount;
QFuture<Data*> future = QtConcurrent::mapped(paths, [thumbnailSize, counterPtr](const QString& path) {
auto data = Data::create(path, thumbnailSize);
counterPtr->fetch_add(1, std::memory_order_relaxed);
return data;
});
@@ -83,18 +82,18 @@ void ImageModel::loadAndProcess(const QStringList& paths) {
emit totalCountChanged();
}
void ImageModel::stop() {
void WallReel::Core::Image::Model::stop() {
if (m_isLoading) {
m_watcher.cancel();
}
}
void ImageModel::_onProgressValueChanged(int value) {
void WallReel::Core::Image::Model::_onProgressValueChanged(int value) {
Q_UNUSED(value);
emit progressChanged();
}
void ImageModel::_onProcessingFinished() {
void WallReel::Core::Image::Model::_onProcessingFinished() {
auto results = m_watcher.future().results();
for (auto& data : results) {
if (data && data->isValid()) {
@@ -116,10 +115,10 @@ void ImageModel::_onProcessingFinished() {
});
}
void ImageModel::sortUpdate() {
void WallReel::Core::Image::Model::sortUpdate() {
const auto type = m_sortConfig.type;
const auto reverse = m_sortConfig.reverse;
std::sort(m_data.begin(), m_data.end(), [type, reverse](ImageData* a, ImageData* b) {
std::sort(m_data.begin(), m_data.end(), [type, reverse](Data* a, Data* b) {
if (!a || !b) {
return false;
}
@@ -127,8 +126,8 @@ void ImageModel::sortUpdate() {
return false;
}
ImageData* first = reverse ? b : a;
ImageData* second = reverse ? a : b;
Data* first = reverse ? b : a;
Data* second = reverse ? a : b;
switch (type) {
case Config::SortType::Name:
@@ -150,7 +149,7 @@ void ImageModel::sortUpdate() {
endResetModel();
}
QVariant ImageModel::dataAt(int index, const QString& roleName) const {
QVariant WallReel::Core::Image::Model::dataAt(int index, const QString& roleName) const {
if (index < 0 || index >= m_data.count()) {
return QVariant();
}
@@ -167,7 +166,7 @@ QVariant ImageModel::dataAt(int index, const QString& roleName) const {
}
}
void ImageModel::_clearData() {
void WallReel::Core::Image::Model::_clearData() {
beginResetModel();
m_provider.clear();
qDeleteAll(m_data);
@@ -175,7 +174,7 @@ void ImageModel::_clearData() {
endResetModel();
}
void ImageModel::selectImage(int index) {
void WallReel::Core::Image::Model::selectImage(int index) {
if (index < 0 || index >= m_data.count()) {
return;
}
@@ -185,7 +184,7 @@ void ImageModel::selectImage(int index) {
}
}
void ImageModel::previewImage(int index) {
void WallReel::Core::Image::Model::previewImage(int index) {
if (index < 0 || index >= m_data.count()) {
return;
}
@@ -6,10 +6,12 @@
#include <QTimer>
#include <atomic>
#include "configmgr.hpp"
#include "imageprovider.hpp"
#include "Config/manager.hpp"
#include "provider.hpp"
class ImageModel : public QAbstractListModel {
namespace WallReel::Core::Image {
class Model : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(bool isLoading READ isLoading NOTIFY isLoadingChanged)
@@ -31,13 +33,13 @@ class ImageModel : public QAbstractListModel {
};
}
ImageModel(
ImageProvider& provider,
Model(
Provider& provider,
const Config::SortConfigItems& sortConfig,
QSize thumbnailSize,
QObject* parent = nullptr);
~ImageModel();
~Model();
bool isLoading() const { return m_isLoading; }
@@ -68,21 +70,21 @@ class ImageModel : public QAbstractListModel {
void isLoadingChanged();
void progressChanged();
void totalCountChanged();
void imageSelected(const ImageData& imageData);
void imagePreviewed(const ImageData& imageData);
void imageSelected(const Data& imageData);
void imagePreviewed(const Data& imageData);
private slots:
void _onProgressValueChanged(int value);
void _onProcessingFinished();
private:
ImageProvider& m_provider;
Provider& m_provider;
const Config::SortConfigItems& m_sortConfig;
QSize m_thumbnailSize;
QVector<ImageData*> m_data;
QVector<Data*> m_data;
QFutureWatcher<ImageData*> m_watcher;
QFutureWatcher<Data*> m_watcher;
bool m_isLoading = false;
std::atomic<int> m_processedCount{0};
@@ -91,4 +93,6 @@ class ImageModel : public QAbstractListModel {
static constexpr int s_IsLoadingUpdateIntervalMs = 50;
};
} // namespace WallReel::Core::Image
#endif // WALLREEL_IMAGEMODEL_HPP
@@ -1,24 +1,24 @@
#include "imageprovider.hpp"
#include "provider.hpp"
QImage ImageProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize) {
QImage WallReel::Core::Image::Provider::requestImage(const QString& id, QSize* size, const QSize& requestedSize) {
QMutexLocker locker(&m_mutex);
if (!m_images.contains(id)) {
return QImage();
}
ImageData* data = m_images[id];
Data* data = m_images[id];
if (size) {
*size = data->getImage().size();
}
return data->getImage();
}
void ImageProvider::insert(ImageData* data) {
void WallReel::Core::Image::Provider::insert(Data* data) {
QMutexLocker locker(&m_mutex);
m_images.insert(data->getId(), data);
}
void ImageProvider::clear() {
void WallReel::Core::Image::Provider::clear() {
QMutexLocker locker(&m_mutex);
m_images.clear();
}
@@ -5,23 +5,27 @@
#include <QMutex>
#include <QQuickImageProvider>
#include "imagedata.hpp"
#include "data.hpp"
class ImageProvider : public QQuickImageProvider {
namespace WallReel::Core::Image {
class Provider : public QQuickImageProvider {
Q_OBJECT
public:
ImageProvider() : QQuickImageProvider(QQuickImageProvider::Image) {}
Provider() : QQuickImageProvider(QQuickImageProvider::Image) {}
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
void insert(ImageData* data);
void insert(Data* data);
void clear();
private:
QMutex m_mutex;
QHash<QString, ImageData*> m_images;
QHash<QString, Data*> m_images;
};
} // namespace WallReel::Core::Image
#endif // WALLREEL_IMAGEPROVIDER_HPP
@@ -6,6 +6,8 @@
#include <QObject>
#include <QString>
namespace WallReel::Core::Palette {
struct ColorItem {
Q_GADGET
Q_PROPERTY(QString name MEMBER name CONSTANT)
@@ -37,7 +39,9 @@ struct PaletteItem {
}
};
Q_DECLARE_METATYPE(ColorItem)
Q_DECLARE_METATYPE(PaletteItem)
} // namespace WallReel::Core::Palette
Q_DECLARE_METATYPE(WallReel::Core::Palette::ColorItem)
Q_DECLARE_METATYPE(WallReel::Core::Palette::PaletteItem)
#endif // WALLREEL_PALETTE_DATA_HPP
@@ -2,7 +2,7 @@
#include "predefined.hpp"
PaletteManager::PaletteManager(
WallReel::Core::Palette::Manager::Manager(
const Config::PaletteConfigItems& config,
QObject* parent) : QObject(parent) {
// The new ones overrides the old ones, use a hashtable to track
@@ -1,16 +1,18 @@
#ifndef WALLREEL_PALETTE_MANAGER_HPP
#define WALLREEL_PALETTE_MANAGER_HPP
#include "../configmgr.hpp"
#include "Config/manager.hpp"
#include "data.hpp"
class PaletteManager : public QObject {
namespace WallReel::Core::Palette {
class Manager : public QObject {
Q_OBJECT
Q_PROPERTY(QList<PaletteItem> availablePalettes READ availablePalettes CONSTANT)
public:
PaletteManager(const Config::PaletteConfigItems& config,
QObject* parent = nullptr);
Manager(const Config::PaletteConfigItems& config,
QObject* parent = nullptr);
const QList<PaletteItem>& availablePalettes() const {
return m_palettes;
@@ -27,4 +29,6 @@ class PaletteManager : public QObject {
QList<PaletteItem> m_palettes;
};
} // namespace WallReel::Core::Palette
#endif // WALLREEL_PALETTE_MANAGER_HPP
@@ -3,6 +3,8 @@
#include "data.hpp"
namespace WallReel::Core::Palette {
inline const QList<PaletteItem> preDefinedPalettes = {
{
@@ -85,4 +87,6 @@ inline const QList<PaletteItem> preDefinedPalettes = {
},
};
} // namespace WallReel::Core::Palette
#endif // WALLREEL_PALETTES_PREDEFINED_HPP
@@ -11,6 +11,8 @@
#include "version.h"
namespace WallReel::Core::Utils {
/**
* @brief Defer execution of a callable until the end of the current scope.
*
@@ -130,4 +132,6 @@ inline QString getConfigDir() {
return configDir;
}
} // namespace WallReel::Core::Utils
#endif // WALLREEL_MISC_HPP
@@ -5,6 +5,8 @@
#include <QRegularExpression>
#include <QString>
namespace WallReel::Core::Utils {
/**
* @brief Replaces {{ key }} style placeholders in a template string with corresponding values from a map.
*
@@ -135,4 +137,6 @@ inline QStringList extractTemplateKeys(const QString& templateStr) {
return keys;
}
} // namespace WallReel::Core::Utils
#endif // TEXTTEMPLATE_HPP
+115
View File
@@ -0,0 +1,115 @@
#include "appoptions.hpp"
#include <QApplication>
#include <QCommandLineOption>
#include <QTextStream>
#include "Utils/misc.hpp"
#include "logger.hpp"
#include "version.h"
namespace WallReel::Core {
// -v --version
void AppOptions::printVersion() {
QTextStream out(stdout);
out << APP_NAME << " version " << APP_VERSION << Qt::endl;
doReturn = true;
}
// -h --help
void AppOptions::printHelp() {
QTextStream out(stdout);
QString helpText = parser.helpText();
auto lines = helpText.split('\n');
for (auto& line : lines) {
if (line.contains("--help-all")) {
// Remove the --help-all option line added by Qt by default
continue;
}
out << line << Qt::endl;
}
doReturn = true;
}
// Print error message and help
void AppOptions::printError() {
if (!errorText.isEmpty()) {
QTextStream out(stderr);
out << errorText << Qt::endl;
printHelp();
}
doReturn = true;
}
AppOptions::AppOptions() = default;
void AppOptions::parseArgs(QApplication& app) {
parser.setApplicationDescription("A small wallpaper utility made with Qt");
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption versionOption = parser.addVersionOption();
QCommandLineOption verboseOption(QStringList() << "V" << "verbose", "Set log level to DEBUG (default is INFO)");
parser.addOption(verboseOption);
QCommandLineOption quietOption(QStringList() << "q" << "quiet", "Suppress all log output");
parser.addOption(quietOption);
QCommandLineOption appendDirOption(QStringList() << "d" << "append-dir", "Append an additional wallpaper search directory", "dir");
parser.addOption(appendDirOption);
QCommandLineOption configFileOption(QStringList() << "c" << "config-file", "Specify a custom configuration file", "file");
parser.addOption(configFileOption);
// Not parser.process(a->arguments()) because we want to handle exit logics ourselves.
// parser.process(...) will do something like exit(...) that will terminate
// the application brutally and produce unwanted warnings.
if (!parser.parse(app.arguments())) {
errorText = parser.errorText();
doReturn = true;
return;
}
if (parser.isSet(versionOption)) {
printVersion();
return;
}
if (parser.isSet(helpOption)) {
printHelp();
return;
}
if (parser.isSet(verboseOption)) {
Logger::setLogLevel(QtDebugMsg);
} else if (parser.isSet(quietOption)) {
Logger::quiet();
} else {
// Default to INFO level
Logger::setLogLevel(QtDebugMsg);
}
for (const QString& dir : parser.values(appendDirOption)) {
if (Utils::checkDir(dir)) {
appendDirs.append(dir);
} else {
errorText = QString("Error: Directory does not exist or is not accessible: %1").arg(dir);
printError();
return;
}
}
if (parser.isSet(configFileOption)) {
QString path = parser.value(configFileOption);
if (Utils::checkFile(path)) {
configPath = path;
} else {
errorText = QString("Error: Config file does not exist or is not accessible: %1").arg(path);
printError();
return;
}
}
}
} // namespace WallReel::Core
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <QCommandLineParser>
#include <QStringList>
class QApplication;
namespace WallReel::Core {
/**
* @brief A class to handle application options.
*/
class AppOptions {
QCommandLineParser parser;
// -v --version
void printVersion();
// -h --help
void printHelp();
// Print error message and help
void printError();
public:
QString configPath;
QStringList appendDirs;
QString errorText;
bool doReturn = false; ///< Indicates whether the application should exit after parsing arguments.
AppOptions();
void parseArgs(QApplication& app);
};
} // namespace WallReel::Core
@@ -71,7 +71,7 @@ static void messageOutput(QtMsgType type, const QMessageLogContext& context, con
}
}
void Logger::init(FILE* stream) {
void WallReel::Core::Logger::init(FILE* stream) {
if (stream) {
delete s_logStream;
s_logStream = new QTextStream(stream);
@@ -81,7 +81,7 @@ void Logger::init(FILE* stream) {
qInstallMessageHandler(messageOutput);
}
void Logger::setLogLevel(QtMsgType level) {
void WallReel::Core::Logger::setLogLevel(QtMsgType level) {
switch (level) {
case QtDebugMsg:
QLoggingCategory::setFilterRules(QString("%1.debug=true").arg(APP_NAME));
@@ -101,23 +101,23 @@ void Logger::setLogLevel(QtMsgType level) {
}
}
void Logger::quiet() {
void WallReel::Core::Logger::quiet() {
QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=false\n%1.critical=false\n%1.fatal=false").arg(APP_NAME));
}
void GeneralLogger::debug(const QString& msg) {
void WallReel::Core::Logger::debug(const QString& msg) {
qCDebug(logMain).noquote() << msg;
}
void GeneralLogger::info(const QString& msg) {
void WallReel::Core::Logger::info(const QString& msg) {
qCInfo(logMain).noquote() << msg;
}
void GeneralLogger::warn(const QString& msg) {
void WallReel::Core::Logger::warn(const QString& msg) {
qCWarning(logMain).noquote() << msg;
}
void GeneralLogger::critical(const QString& msg) {
void WallReel::Core::Logger::critical(const QString& msg) {
qCCritical(logMain).noquote() << msg;
}
@@ -7,20 +7,17 @@
Q_DECLARE_LOGGING_CATEGORY(logMain)
namespace GeneralLogger {
void debug(const QString& msg);
void info(const QString& msg);
void warn(const QString& msg);
void critical(const QString& msg);
} // namespace GeneralLogger
namespace WallReel::Core {
class Logger {
public:
static void debug(const QString& msg);
static void info(const QString& msg);
static void warn(const QString& msg);
static void critical(const QString& msg);
/**
* @brief Initialize the logger and set the output stream.
*
@@ -42,4 +39,6 @@ class Logger {
static void quiet();
};
} // namespace WallReel::Core
#endif // WALLREEL_LOGGER_HPP
+14 -16
View File
@@ -3,12 +3,10 @@
#include <QColor>
#include <iostream>
#include "utils/logger.hpp"
#include "utils/texttemplate.hpp"
#include "Utils/texttemplate.hpp"
#include "logger.hpp"
using namespace GeneralLogger;
WallpaperService::WallpaperService(
WallReel::Core::WallpaperService::WallpaperService(
const Config::ActionConfigItems& actionConfig,
QObject* parent)
: QObject(parent), m_actionConfig(actionConfig) {
@@ -50,28 +48,28 @@ WallpaperService::WallpaperService(
});
}
void WallpaperService::preview(const ImageData& imageData) {
void WallReel::Core::WallpaperService::preview(const Image::Data& imageData) {
m_pendingImageData = &imageData;
m_previewDebounceTimer->start();
}
void WallpaperService::select(const ImageData& imageData) {
void WallReel::Core::WallpaperService::select(const Image::Data& imageData) {
if (m_selectProcess->state() != QProcess::NotRunning) {
warn("Previous select command is still running. Ignoring new command.");
Logger::warn("Previous select command is still running. Ignoring new command.");
return;
}
_doSelect(imageData);
}
void WallpaperService::restore() {
void WallReel::Core::WallpaperService::restore() {
if (m_restoreProcess->state() != QProcess::NotRunning) {
warn("Previous restore command is still running. Ignoring new command.");
Logger::warn("Previous restore command is still running. Ignoring new command.");
return;
}
_doRestore();
}
void WallpaperService::_doPreview(const ImageData& imageData) {
void WallReel::Core::WallpaperService::_doPreview(const Image::Data& imageData) {
QString path = imageData.getFullPath();
if (path.isEmpty()) {
@@ -86,7 +84,7 @@ void WallpaperService::_doPreview(const ImageData& imageData) {
{"path", path},
{"name", imageData.getFileName()},
};
auto command = renderTemplate(m_actionConfig.onPreview, variables);
auto command = Utils::renderTemplate(m_actionConfig.onPreview, variables);
if (command.isEmpty()) {
return;
}
@@ -98,7 +96,7 @@ void WallpaperService::_doPreview(const ImageData& imageData) {
m_previewProcess->start("sh", QStringList() << "-c" << command);
}
void WallpaperService::_doSelect(const ImageData& imageData) {
void WallReel::Core::WallpaperService::_doSelect(const Image::Data& imageData) {
QString path = imageData.getFullPath();
if (path.isEmpty()) {
@@ -113,19 +111,19 @@ void WallpaperService::_doSelect(const ImageData& imageData) {
{"path", path},
{"name", imageData.getFileName()},
};
auto command = renderTemplate(m_actionConfig.onSelected, variables);
auto command = Utils::renderTemplate(m_actionConfig.onSelected, variables);
if (command.isEmpty()) {
return;
}
m_selectProcess->start("sh", QStringList() << "-c" << command);
}
void WallpaperService::_doRestore() {
void WallReel::Core::WallpaperService::_doRestore() {
if (m_actionConfig.onRestore.isEmpty()) {
return;
}
const QString command = renderTemplate(m_actionConfig.onRestore, m_actionConfig.saveState);
const QString command = Utils::renderTemplate(m_actionConfig.onRestore, m_actionConfig.saveState);
if (command.isEmpty()) {
return;
}
+12 -8
View File
@@ -4,8 +4,10 @@
#include <QProcess>
#include <QTimer>
#include "configmgr.hpp"
#include "imagedata.hpp"
#include "Config/manager.hpp"
#include "Image/data.hpp"
namespace WallReel::Core {
class WallpaperService : public QObject {
Q_OBJECT
@@ -16,9 +18,9 @@ class WallpaperService : public QObject {
QObject* parent = nullptr);
public slots:
void preview(const ImageData& imageData); // execute after 500ms of inactivity
void select(const ImageData& imageData); // execute immediately, ignore if already running
void restore(); // execute immediately, ignore if already running
void preview(const Image::Data& imageData); // execute after 500ms of inactivity
void select(const Image::Data& imageData); // execute immediately, ignore if already running
void restore(); // execute immediately, ignore if already running
signals:
void previewCompleted();
@@ -26,16 +28,18 @@ class WallpaperService : public QObject {
void restoreCompleted();
private:
void _doPreview(const ImageData& imageData);
void _doSelect(const ImageData& imageData);
void _doPreview(const Image::Data& imageData);
void _doSelect(const Image::Data& imageData);
void _doRestore();
const Config::ActionConfigItems& m_actionConfig;
QTimer* m_previewDebounceTimer;
const ImageData* m_pendingImageData;
const Image::Data* m_pendingImageData;
QProcess* m_previewProcess;
QProcess* m_selectProcess;
QProcess* m_restoreProcess;
};
} // namespace WallReel::Core
#endif // WALLREEL_WALLPAPERSERVICE_HPP
+42 -165
View File
@@ -1,149 +1,29 @@
#include <qobject.h>
#include <QApplication>
#include <QCommandLineParser>
#include <QDir>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStandardPaths>
#include <QTextStream>
#include "Core/configmgr.hpp"
#include "Core/imagemodel.hpp"
#include "Core/imageprovider.hpp"
#include "Core/palette/data.hpp"
#include "Core/palette/manager.hpp"
#include "Core/utils/logger.hpp"
#include "Core/utils/misc.hpp"
#include "Core/Config/manager.hpp"
#include "Core/Image/model.hpp"
#include "Core/Image/provider.hpp"
#include "Core/Palette/data.hpp"
#include "Core/Palette/manager.hpp"
#include "Core/Utils/misc.hpp"
#include "Core/appoptions.hpp"
#include "Core/logger.hpp"
#include "Core/wallpaperservice.hpp"
#include "version.h"
/**
* @brief A static & single-instance class to handle application options.
*
*/
static class AppOptions {
QCommandLineParser parser{};
// The following 3 functions handle specific command line options
// and mark doReturn as true to indicate that the application should exit
// after parsing arguments.
// -v --version
void printVersion() {
QTextStream out(stdout);
out << APP_NAME << " version " << APP_VERSION << Qt::endl;
doReturn = true;
}
// -h --help
void printHelp() {
QTextStream out(stdout);
QString helpText = parser.helpText();
auto lines = helpText.split('\n');
for (auto& line : lines) {
if (line.contains("--help-all")) {
// Remove the --help-all option line added by Qt by default
continue;
}
out << line << Qt::endl;
}
doReturn = true;
}
// Print error message and help
void printError() {
if (!errorText.isEmpty()) {
QTextStream out(stderr);
out << errorText << Qt::endl;
printHelp();
}
doReturn = true;
}
public:
QString configPath = "";
QStringList appendDirs;
QString errorText = "";
bool doReturn = false; ///< Indicates whether the application should exit after parsing arguments.
void parseArgs(QApplication* a) {
parser.setApplicationDescription("A small wallpaper utility made with Qt");
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption versionOption = parser.addVersionOption();
QCommandLineOption verboseOption(QStringList() << "V" << "verbose", "Set log level to DEBUG (default is INFO)");
parser.addOption(verboseOption);
QCommandLineOption quietOption(QStringList() << "q" << "quiet", "Suppress all log output");
parser.addOption(quietOption);
QCommandLineOption appendDirOption(QStringList() << "d" << "append-dir", "Append an additional wallpaper search directory", "dir");
parser.addOption(appendDirOption);
QCommandLineOption configFileOption(QStringList() << "c" << "config-file", "Specify a custom configuration file", "file");
parser.addOption(configFileOption);
// Not parser.process(a->arguments()) because we want to handle exit logics ourselves.
// parser.process(...) will do something like exit(...) that will terminate
// the application brutally and produce unwanted warnings.
if (!parser.parse(a->arguments())) {
errorText = parser.errorText();
doReturn = true;
return;
}
if (parser.isSet(versionOption)) {
printVersion();
return;
}
if (parser.isSet(helpOption)) {
printHelp();
return;
}
if (parser.isSet(verboseOption)) {
Logger::setLogLevel(QtDebugMsg);
} else if (parser.isSet(quietOption)) {
Logger::quiet();
} else {
// Default to INFO level
Logger::setLogLevel(QtDebugMsg);
}
for (const QString& dir : parser.values(appendDirOption)) {
if (checkDir(dir)) {
appendDirs.append(dir);
} else {
errorText = QString("Error: Directory does not exist or is not accessible: %1").arg(dir);
printError();
return;
}
}
if (parser.isSet(configFileOption)) {
QString path = parser.value(configFileOption);
if (checkFile(path)) {
configPath = path;
} else {
errorText = QString("Error: Config file does not exist or is not accessible: %1").arg(path);
printError();
return;
}
}
}
} s_options;
using namespace WallReel::Core;
int main(int argc, char* argv[]) {
AppOptions s_options;
QApplication a(argc, argv);
a.setApplicationName(APP_NAME);
a.setApplicationVersion(APP_VERSION);
Logger::init();
s_options.parseArgs(&a);
s_options.parseArgs(a);
if (s_options.doReturn) {
return s_options.errorText.isEmpty() ? 0 : 1;
@@ -151,50 +31,33 @@ int main(int argc, char* argv[]) {
QQmlApplicationEngine engine;
ImageProvider* imageProvider = new ImageProvider();
auto* imageProvider = new Image::Provider();
engine.addImageProvider(QLatin1String("processed"), imageProvider);
auto config = new Config(
::getConfigDir(),
auto config = new Config::Manager(
Utils::getConfigDir(),
s_options.appendDirs,
s_options.configPath,
imageProvider);
auto paletteMgr = new PaletteManager(
config->getPaletteConfig(),
&a);
engine.rootContext()->setContextProperty("PaletteManager", paletteMgr);
qRegisterMetaType<PaletteItem>();
qRegisterMetaType<ColorItem>();
auto imageModel = new ImageModel(
*imageProvider,
config->getSortConfig(),
config->getFocusImageSize(),
config);
auto wallpaperService = new WallpaperService(
config->getActionConfig(),
config);
QObject::connect(
imageModel,
&ImageModel::imageSelected,
wallpaperService,
&WallpaperService::select);
QObject::connect(
imageModel,
&ImageModel::imagePreviewed,
wallpaperService,
&WallpaperService::preview);
qmlRegisterSingletonInstance(
COREMODULE_URI,
MODULE_VERSION_MAJOR,
MODULE_VERSION_MINOR,
"Config",
config);
auto paletteMgr = new Palette::Manager(
config->getPaletteConfig(),
&a);
engine.rootContext()->setContextProperty("PaletteManager", paletteMgr);
qRegisterMetaType<Palette::PaletteItem>("PaletteItem");
qRegisterMetaType<Palette::ColorItem>("ColorItem");
auto imageModel = new Image::Model(
*imageProvider,
config->getSortConfig(),
config->getFocusImageSize(),
config);
qmlRegisterSingletonInstance(
COREMODULE_URI,
MODULE_VERSION_MAJOR,
@@ -202,6 +65,20 @@ int main(int argc, char* argv[]) {
"ImageModel",
imageModel);
auto wallpaperService = new WallpaperService(
config->getActionConfig(),
config);
QObject::connect(
imageModel,
&Image::Model::imageSelected,
wallpaperService,
&WallpaperService::select);
QObject::connect(
imageModel,
&Image::Model::imagePreviewed,
wallpaperService,
&WallpaperService::preview);
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreationFailed,