feat: configurable sizes

This commit is contained in:
2025-08-05 20:14:37 +02:00
parent 56d5e66d6f
commit 1100910f62
10 changed files with 256 additions and 152 deletions
+7
View File
@@ -14,5 +14,12 @@
}, },
"actions": { "actions": {
"confirm": "~/.scripts/change-wallpaper.fish \"%1\"" "confirm": "~/.scripts/change-wallpaper.fish \"%1\""
},
"style": {
"aspect_ratio": 1.6,
"image_width": 320,
"image_focus_width": 480,
"window_width": 750,
"window_height": 500
} }
} }
+103 -44
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:34:52 * @Date: 2025-08-05 01:34:52
* @LastEditTime: 2025-08-05 17:26:33 * @LastEditTime: 2025-08-05 20:11:11
* @Description: Configuration manager. * @Description: Configuration manager.
*/ */
#include "config.h" #include "config.h"
@@ -19,22 +19,6 @@ using namespace GeneralLogger;
static QString expandPath(const QString &path); static QString expandPath(const QString &path);
const QString Config::s_DefaultConfigFileName = "config.json";
Config::Config(const QString &configDir, const QStringList &searchDirs, QObject *parent) : QObject(parent) {
info(QString("Loading configuration from: %1").arg(configDir));
_loadConfig(configDir + QDir::separator() + s_DefaultConfigFileName);
info(QString("Additional search directories: %1").arg(searchDirs.join(", ")));
m_configItems.wallpaperDirs.append(searchDirs);
info("Loading wallpapers ...");
_loadWallpapers();
}
Config::~Config() {
}
void Config::_loadConfig(const QString &configPath) { void Config::_loadConfig(const QString &configPath) {
QFile configFile(configPath); QFile configFile(configPath);
if (!configFile.open(QIODevice::ReadOnly)) { if (!configFile.open(QIODevice::ReadOnly)) {
@@ -50,42 +34,117 @@ void Config::_loadConfig(const QString &configPath) {
return; return;
} }
static const auto parseJsonArray = [](const QJsonObject &obj, const QString &key, QStringList &list) { const auto jsonObj = jsonDoc.object();
if (obj.contains(key) && obj[key].isArray()) {
QJsonArray array = obj[key].toArray(); struct ConfigMapping {
for (const QJsonValue &value : array) { QString path;
if (value.isString()) { QString key;
list.append(::expandPath(value.toString())); std::function<void(const QJsonValue &)> parser;
};
static const auto parseJsonArray = [](const QJsonValue &val, QStringList &list) {
if (val.isArray()) {
for (const auto &item : val.toArray()) {
if (item.isString()) {
list.append(::expandPath(item.toString()));
} }
} }
} else {
warn(QString("Key '%1' not found or not an array in config").arg(key));
} }
}; };
const auto jsonObj = jsonDoc.object(); std::vector<ConfigMapping>
if (!jsonObj.contains("wallpaper") || !jsonObj["wallpaper"].isObject()) { mappings = {
warn("Key 'wallpaper' not fount or not an object in config"); {"wallpaper.paths", "paths", [this](const QJsonValue &val) {
} else { parseJsonArray(val, m_configItems.wallpaperPaths);
const auto wallpaperObj = jsonObj.value("wallpaper").toObject(); }},
parseJsonArray(wallpaperObj, "paths", m_configItems.wallpaperPaths); {"wallpaper.dirs", "dirs", [this](const QJsonValue &val) {
parseJsonArray(wallpaperObj, "dirs", m_configItems.wallpaperDirs); parseJsonArray(val, m_configItems.wallpaperDirs);
parseJsonArray(wallpaperObj, "excludes", m_configItems.wallpaperExcludes); }},
} {"wallpaper.excludes", "excludes", [this](const QJsonValue &val) {
parseJsonArray(val, m_configItems.wallpaperExcludes);
}},
{"actions.confirm", "confirm", [this](const QJsonValue &val) {
if (val.isString()) {
m_configItems.actionsConfirm = ::expandPath(val.toString());
info(QString("Action confirm: %1").arg(m_configItems.actionsConfirm));
}
}},
{"style.aspect_ratio", "aspect_ratio", [this](const QJsonValue &val) {
if (val.isDouble() && val.toDouble() > 0) {
m_configItems.styleAspectRatio = val.toDouble();
info(QString("Aspect ratio: %1").arg(m_configItems.styleAspectRatio));
}
}},
{"style.image_width", "image_width", [this](const QJsonValue &val) {
if (val.isDouble() && val.toDouble() > 0) {
m_configItems.styleImageWidth = val.toInt();
info(QString("Image width: %1").arg(m_configItems.styleImageWidth));
}
}},
{"style.image_focus_width", "image_focus_width", [this](const QJsonValue &val) {
if (val.isDouble() && val.toDouble() > 0) {
m_configItems.styleImageFocusWidth = val.toInt();
info(QString("Image focus width: %1").arg(m_configItems.styleImageFocusWidth));
}
}},
{"style.window_width", "window_width", [this](const QJsonValue &val) {
if (val.isDouble() && val.toDouble() > 0) {
m_configItems.styleWindowWidth = val.toInt();
info(QString("Window width: %1").arg(m_configItems.styleWindowWidth));
}
}},
{"style.window_height", "window_height", [this](const QJsonValue &val) {
if (val.isDouble() && val.toDouble() > 0) {
m_configItems.styleWindowHeight = val.toInt();
info(QString("Window height: %1").arg(m_configItems.styleWindowHeight));
}
}},
};
if (!jsonObj.contains("actions") || !jsonObj["actions"].isObject()) { // 统一解析
warn("Key 'actions' not found or not an object in config"); for (const auto &mapping : mappings) {
} else { ([&mapping, &jsonObj]() {
const auto actionsObj = jsonObj.value("actions").toObject(); auto pathParts = mapping.path.split('.');
if (actionsObj.contains("confirm") && actionsObj["confirm"].isString()) {
m_configItems.actionsConfirm = ::expandPath(actionsObj["confirm"].toString()); QJsonObject currentObj = jsonObj;
info(QString("Action on confirm: %1").arg(m_configItems.actionsConfirm)); QJsonValue targetValue;
} else {
warn("Key 'confirm' not found or not a string in 'actions'"); for (int i = 0; i < pathParts.size() - 1; ++i) {
} if (currentObj.contains(pathParts[i]) && currentObj[pathParts[i]].isObject()) {
currentObj = currentObj[pathParts[i]].toObject();
} else {
warn(QString("Path '%1' not found").arg(pathParts.mid(0, i + 1).join('.')));
return;
}
}
// 获取目标值
const QString &finalKey = pathParts.last();
if (currentObj.contains(finalKey)) {
mapping.parser(currentObj[finalKey]);
} else {
warn(QString("Key '%1' not found in '%2'").arg(finalKey).arg(mapping.path));
}
})();
} }
} }
const QString Config::s_DefaultConfigFileName = "config.json";
Config::Config(const QString &configDir, const QStringList &searchDirs, QObject *parent) : QObject(parent) {
info(QString("Loading configuration from: %1").arg(configDir));
_loadConfig(configDir + QDir::separator() + s_DefaultConfigFileName);
info(QString("Additional search directories: %1").arg(searchDirs.join(", ")));
m_configItems.wallpaperDirs.append(searchDirs);
info("Loading wallpapers ...");
_loadWallpapers();
}
Config::~Config() {
}
void Config::_loadWallpapers() { void Config::_loadWallpapers() {
m_wallpapers.clear(); m_wallpapers.clear();
+17 -2
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:34:52 * @Date: 2025-08-05 01:34:52
* @LastEditTime: 2025-08-05 17:26:25 * @LastEditTime: 2025-08-05 20:12:47
* @Description: Configuration manager. * @Description: Configuration manager.
*/ */
#ifndef CONFIG_H #ifndef CONFIG_H
@@ -27,6 +27,16 @@ class Config : public QObject {
[[nodiscard]] const QString& getActionsConfirm() const { return m_configItems.actionsConfirm; } [[nodiscard]] const QString& getActionsConfirm() const { return m_configItems.actionsConfirm; }
[[nodiscard]] double getStyleAspectRatio() const { return m_configItems.styleAspectRatio; }
[[nodiscard]] int getStyleImageWidth() const { return m_configItems.styleImageWidth; }
[[nodiscard]] int getStyleImageFocusWidth() const { return m_configItems.styleImageFocusWidth; }
[[nodiscard]] int getStyleWindowWidth() const { return m_configItems.styleWindowWidth; }
[[nodiscard]] int getStyleWindowHeight() const { return m_configItems.styleWindowHeight; }
static const QString s_DefaultConfigFileName; static const QString s_DefaultConfigFileName;
private: private:
@@ -35,11 +45,16 @@ class Config : public QObject {
void _loadWallpapers(); void _loadWallpapers();
private: private:
struct ConfigItems { struct _ConfigItems {
QStringList wallpaperPaths; QStringList wallpaperPaths;
QStringList wallpaperDirs; QStringList wallpaperDirs;
QStringList wallpaperExcludes; QStringList wallpaperExcludes;
QString actionsConfirm; QString actionsConfirm;
double styleAspectRatio = 1.6;
int styleImageWidth = 320;
int styleImageFocusWidth = 480;
int styleWindowWidth = 800;
int styleWindowHeight = 600;
} m_configItems; } m_configItems;
QStringList m_wallpapers; QStringList m_wallpapers;
+6
View File
@@ -10,6 +10,12 @@
<height>300</height> <height>300</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
+1 -19
View File
@@ -35,17 +35,7 @@
<set>QMainWindow::DockOption::AllowTabbedDocks|QMainWindow::DockOption::AnimatedDocks</set> <set>QMainWindow::DockOption::AllowTabbedDocks|QMainWindow::DockOption::AnimatedDocks</set>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="mainLayout">
<item>
<widget class="ImagesCarousel" name="carousel" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item> <item>
<widget class="QWidget" name="actions" native="true"> <widget class="QWidget" name="actions" native="true">
<property name="minimumSize"> <property name="minimumSize">
@@ -100,14 +90,6 @@
</layout> </layout>
</widget> </widget>
</widget> </widget>
<customwidgets>
<customwidget>
<class>ImagesCarousel</class>
<extends>QWidget</extends>
<header>images_carousel.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>
+54 -53
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53 * @Date: 2025-08-05 01:22:53
* @LastEditTime: 2025-08-05 17:25:59 * @LastEditTime: 2025-08-05 20:06:23
* @Description: Animated carousel widget for displaying and selecting images. * @Description: Animated carousel widget for displaying and selecting images.
*/ */
#include "images_carousel.h" #include "images_carousel.h"
@@ -21,20 +21,27 @@
using namespace GeneralLogger; using namespace GeneralLogger;
ImagesCarousel::ImagesCarousel(QWidget* parent) ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
const int itemWidth,
const int itemFocusWidth,
QWidget* parent)
: QWidget(parent), : QWidget(parent),
ui(new Ui::ImagesCarousel), ui(new Ui::ImagesCarousel),
m_updateTimer(new QTimer(this)), m_updateTimer(new QTimer(this)),
m_scrollAnimation(nullptr) { m_scrollAnimation(nullptr),
m_itemWidth(itemWidth),
m_itemHeight(static_cast<int>(itemWidth / itemAspectRatio)),
m_itemFocusWidth(itemFocusWidth),
m_itemFocusHeight(static_cast<int>(itemFocusWidth / itemAspectRatio)) {
ui->setupUi(this); ui->setupUi(this);
connect(m_updateTimer, &QTimer::timeout, this, &ImagesCarousel::updateImages); connect(m_updateTimer, &QTimer::timeout, this, &ImagesCarousel::_updateImages);
m_updateTimer->start(100); m_updateTimer->start(100);
} }
ImagesCarousel::~ImagesCarousel() { ImagesCarousel::~ImagesCarousel() {
delete ui; delete ui;
for (auto item : m_imageQueue) { for (auto item : std::as_const(m_imageQueue)) {
delete item; delete item;
} }
if (m_scrollAnimation) { if (m_scrollAnimation) {
@@ -49,22 +56,26 @@ void ImagesCarousel::appendImage(const QString& path) {
} }
ImageLoader::ImageLoader(const QString& path, ImagesCarousel* carousel) ImageLoader::ImageLoader(const QString& path, ImagesCarousel* carousel)
: m_path(path), m_carousel(carousel) { : m_path(path),
m_carousel(carousel),
m_initWidth(carousel->m_itemFocusWidth),
m_initHeight(carousel->m_itemFocusHeight) {
setAutoDelete(true); setAutoDelete(true);
} }
void ImagesCarousel::addImageToQueue(const ImageData* data) { void ImagesCarousel::_addImageToQueue(const ImageData* data) {
QMutexLocker locker(&m_queueMutex); QMutexLocker locker(&m_queueMutex);
auto imageItem = new ImageItem(data, auto imageItem = new ImageItem(
s_itemWidth, data,
s_itemHeight, m_itemWidth,
s_itemFocusWidth, m_itemHeight,
s_itemFocusHeight, m_itemFocusWidth,
this); m_itemFocusHeight,
this);
m_imageQueue.enqueue(imageItem); m_imageQueue.enqueue(imageItem);
} }
void ImagesCarousel::updateImages() { void ImagesCarousel::_updateImages() {
QMutexLocker locker(&m_queueMutex); QMutexLocker locker(&m_queueMutex);
int processCount = 0; int processCount = 0;
@@ -72,30 +83,31 @@ void ImagesCarousel::updateImages() {
ImageItem* item = m_imageQueue.dequeue(); ImageItem* item = m_imageQueue.dequeue();
ui->scrollAreaWidgetContents->layout()->addWidget(item); ui->scrollAreaWidgetContents->layout()->addWidget(item);
m_loadedImages.append(item); m_loadedImages.append(item);
// focus first image
if (m_loadedImages.size() == 1) { if (m_loadedImages.size() == 1) {
item->focusImage(); item->setFocus(true);
} else { } else {
item->unfocusImage(); item->setFocus(false);
} }
processCount++; processCount++;
} }
} }
void ImageLoader::run() { void ImageLoader::run() {
auto data = new ImageData(m_path); auto data = new ImageData(m_path, m_initWidth, m_initHeight);
QMetaObject::invokeMethod(m_carousel, QMetaObject::invokeMethod(m_carousel,
"addImageToQueue", "_addImageToQueue",
Qt::QueuedConnection, Qt::QueuedConnection,
Q_ARG(const ImageData*, data)); Q_ARG(const ImageData*, data));
} }
ImageData::ImageData(const QString& p) : path(p) { ImageData::ImageData(const QString& p, const int initWidth, const int initHeight) : path(p) {
path = p; path = p;
if (!pixmap.load(p)) { if (!pixmap.load(p)) {
warn(QString("Failed to load image from path: %1").arg(p)); warn(QString("Failed to load image from path: %1").arg(p));
} }
// resize in "cover" mode // resize in "cover" mode
const QSize targetSize(ImagesCarousel::s_itemWidth, ImagesCarousel::s_itemHeight); const QSize targetSize(initWidth, initHeight);
pixmap = pixmap.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); pixmap = pixmap.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Crop to center // Crop to center
@@ -105,34 +117,34 @@ ImageData::ImageData(const QString& p) : path(p) {
} }
void ImagesCarousel::focusNextImage() { void ImagesCarousel::focusNextImage() {
unfocusCurrImage(); _unfocusCurrImage();
if (m_loadedImages.size() <= 1) return; if (m_loadedImages.size() <= 1) return;
m_currentIndex++; m_currentIndex++;
if (m_currentIndex >= m_loadedImages.size()) { if (m_currentIndex >= m_loadedImages.size()) {
m_currentIndex = 0; m_currentIndex = 0;
} }
focusCurrImage(); _focusCurrImage();
} }
void ImagesCarousel::focusPrevImage() { void ImagesCarousel::focusPrevImage() {
if (m_loadedImages.size() <= 1) return; if (m_loadedImages.size() <= 1) return;
unfocusCurrImage(); _unfocusCurrImage();
m_currentIndex--; m_currentIndex--;
if (m_currentIndex < 0) { if (m_currentIndex < 0) {
m_currentIndex = m_loadedImages.size() - 1; m_currentIndex = m_loadedImages.size() - 1;
} }
focusCurrImage(); _focusCurrImage();
} }
void ImagesCarousel::unfocusCurrImage() { void ImagesCarousel::_unfocusCurrImage() {
m_loadedImages[m_currentIndex]->unfocusImage(); m_loadedImages[m_currentIndex]->setFocus(false);
} }
void ImagesCarousel::focusCurrImage() { void ImagesCarousel::_focusCurrImage() {
m_loadedImages[m_currentIndex]->focusImage(); m_loadedImages[m_currentIndex]->setFocus(true);
auto hScrollBar = ui->scrollArea->horizontalScrollBar(); auto hScrollBar = ui->scrollArea->horizontalScrollBar();
int spacing = ui->scrollAreaWidgetContents->layout()->spacing(); int spacing = ui->scrollAreaWidgetContents->layout()->spacing();
int centerOffset = (s_itemWidth + spacing) * m_currentIndex + s_itemFocusWidth / 2 - spacing; int centerOffset = (m_itemWidth + spacing) * m_currentIndex + m_itemFocusWidth / 2 - spacing;
int leftOffset = centerOffset - ui->scrollArea->width() / 2; int leftOffset = centerOffset - ui->scrollArea->width() / 2;
if (leftOffset < 0) { if (leftOffset < 0) {
leftOffset = 0; leftOffset = 0;
@@ -162,12 +174,21 @@ ImageItem::ImageItem(const ImageData* data,
m_data(data), m_data(data),
m_itemSize(itemWidth, itemHeight), m_itemSize(itemWidth, itemHeight),
m_itemFocusSize(itemFocusWidth, itemFocusHeight) { m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
setPixmap(data->pixmap);
setFixedSize(ImagesCarousel::s_itemWidth, ImagesCarousel::s_itemHeight);
setScaledContents(true); setScaledContents(true);
setPixmap(data->pixmap);
setFixedSize(itemWidth, itemHeight);
} }
void ImageItem::focusImage() { ImageItem::~ImageItem() {
if (m_scaleAnimation) {
m_scaleAnimation->stop();
delete m_scaleAnimation;
m_scaleAnimation = nullptr;
}
delete m_data;
}
void ImageItem::setFocus(bool focus) {
if (m_scaleAnimation) { if (m_scaleAnimation) {
m_scaleAnimation->stop(); m_scaleAnimation->stop();
delete m_scaleAnimation; delete m_scaleAnimation;
@@ -176,27 +197,7 @@ void ImageItem::focusImage() {
m_scaleAnimation = new QPropertyAnimation(this, "size"); m_scaleAnimation = new QPropertyAnimation(this, "size");
m_scaleAnimation->setDuration(ImagesCarousel::s_animationDuration); m_scaleAnimation->setDuration(ImagesCarousel::s_animationDuration);
m_scaleAnimation->setStartValue(size()); m_scaleAnimation->setStartValue(size());
m_scaleAnimation->setEndValue(m_itemFocusSize); m_scaleAnimation->setEndValue(focus ? m_itemFocusSize : m_itemSize);
m_scaleAnimation->setEasingCurve(QEasingCurve::OutCubic);
connect(m_scaleAnimation,
&QPropertyAnimation::valueChanged,
this,
[this](const QVariant& value) {
setFixedSize(value.toSize());
});
m_scaleAnimation->start();
}
void ImageItem::unfocusImage() {
if (m_scaleAnimation) {
m_scaleAnimation->stop();
delete m_scaleAnimation;
m_scaleAnimation = nullptr;
}
m_scaleAnimation = new QPropertyAnimation(this, "size");
m_scaleAnimation->setDuration(ImagesCarousel::s_animationDuration);
m_scaleAnimation->setStartValue(size());
m_scaleAnimation->setEndValue(m_itemSize);
m_scaleAnimation->setEasingCurve(QEasingCurve::OutCubic); m_scaleAnimation->setEasingCurve(QEasingCurve::OutCubic);
connect(m_scaleAnimation, connect(m_scaleAnimation,
&QPropertyAnimation::valueChanged, &QPropertyAnimation::valueChanged,
+37 -17
View File
@@ -1,12 +1,14 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53 * @Date: 2025-08-05 01:22:53
* @LastEditTime: 2025-08-05 17:25:34 * @LastEditTime: 2025-08-05 19:47:43
* @Description: Animated carousel widget for displaying and selecting images. * @Description: Animated carousel widget for displaying and selecting images.
*/ */
#ifndef IMAGES_CAROUSEL_H #ifndef IMAGES_CAROUSEL_H
#define IMAGES_CAROUSEL_H #define IMAGES_CAROUSEL_H
#include <qtmetamacros.h>
#include <QKeyEvent> #include <QKeyEvent>
#include <QLabel> #include <QLabel>
#include <QMutex> #include <QMutex>
@@ -22,15 +24,20 @@
class ImagesCarousel; class ImagesCarousel;
/**
* @brief Data structure to hold image information
* and can be safely created and passed between threads.
*/
struct ImageData { struct ImageData {
QString path; QString path;
QPixmap pixmap; QPixmap pixmap;
ImageData() = default; explicit ImageData(const QString& p, const int initWidth, const int initHeight);
explicit ImageData(const QString& p);
}; };
/**
* @brief Image label that displays an image
*/
class ImageItem : public QLabel { class ImageItem : public QLabel {
Q_OBJECT Q_OBJECT
@@ -42,13 +49,14 @@ class ImageItem : public QLabel {
const int itemFocusHeight, const int itemFocusHeight,
QWidget* parent = nullptr); QWidget* parent = nullptr);
~ImageItem() override;
[[nodiscard]] const QString& getPath() const { return m_data->path; } [[nodiscard]] const QString& getPath() const { return m_data->path; }
[[nodiscard]] const QPixmap& getPixmap() const { return m_data->pixmap; } [[nodiscard]] const QPixmap& getPixmap() const { return m_data->pixmap; }
public slots: public:
void focusImage(); void setFocus(bool focus = true);
void unfocusImage();
private: private:
const ImageData* m_data; const ImageData* m_data;
@@ -65,6 +73,8 @@ class ImageLoader : public QRunnable {
private: private:
QString m_path; QString m_path;
ImagesCarousel* m_carousel; ImagesCarousel* m_carousel;
const int m_initWidth;
const int m_initHeight;
}; };
namespace Ui { namespace Ui {
@@ -74,14 +84,15 @@ class ImagesCarousel;
class ImagesCarousel : public QWidget { class ImagesCarousel : public QWidget {
Q_OBJECT Q_OBJECT
friend void ImageLoader::run();
public: public:
explicit ImagesCarousel(QWidget* parent = nullptr); explicit ImagesCarousel(const double itemAspectRatio,
const int itemWidth,
const int itemFocusWidth,
QWidget* parent = nullptr);
~ImagesCarousel(); ~ImagesCarousel();
static constexpr int s_itemWidth = 320;
static constexpr int s_itemHeight = 200;
static constexpr int s_itemFocusWidth = 480;
static constexpr int s_itemFocusHeight = 300;
static constexpr int s_animationDuration = 300; static constexpr int s_animationDuration = 300;
[[nodiscard]] QString getCurrentImagePath() const { [[nodiscard]] QString getCurrentImagePath() const {
@@ -91,16 +102,25 @@ class ImagesCarousel : public QWidget {
return m_loadedImages[m_currentIndex]->getPath(); return m_loadedImages[m_currentIndex]->getPath();
} }
const int m_itemWidth = 320;
const int m_itemHeight = 180;
const int m_itemFocusWidth = 480;
const int m_itemFocusHeight = 270;
public slots: public slots:
void addImageToQueue(const ImageData* data);
void appendImage(const QString& path);
void focusNextImage(); void focusNextImage();
void focusPrevImage(); void focusPrevImage();
void unfocusCurrImage();
void focusCurrImage();
private slots: private slots:
void updateImages(); void _unfocusCurrImage();
public:
void appendImage(const QString& path);
private:
Q_INVOKABLE void _addImageToQueue(const ImageData* data);
void _focusCurrImage();
void _updateImages();
private: private:
Ui::ImagesCarousel* ui; Ui::ImagesCarousel* ui;
+5 -2
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 00:37:58 * @Date: 2025-08-05 00:37:58
* @LastEditTime: 2025-08-05 17:34:37 * @LastEditTime: 2025-08-05 19:42:07
* @Description: Entry point. * @Description: Entry point.
*/ */
#include <qapplication.h> #include <qapplication.h>
@@ -11,6 +11,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QTextStream> #include <QTextStream>
#include "config.h"
#include "logger.h" #include "logger.h"
#include "main_window.h" #include "main_window.h"
@@ -28,7 +29,9 @@ static QString getConfigDir() {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
QApplication a(argc, argv); QApplication a(argc, argv);
MainWindow w(::getConfigDir()); Config config(getConfigDir());
MainWindow w(config);
w.show(); w.show();
return a.exec(); return a.exec();
+21 -12
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 00:37:58 * @Date: 2025-08-05 00:37:58
* @LastEditTime: 2025-08-05 17:40:35 * @LastEditTime: 2025-08-05 20:12:40
* @Description: MainWindow implementation. * @Description: MainWindow implementation.
*/ */
#include "main_window.h" #include "main_window.h"
@@ -17,11 +17,8 @@
using namespace GeneralLogger; using namespace GeneralLogger;
MainWindow::MainWindow(const QString &configDir, QWidget *parent) MainWindow::MainWindow(const Config &config, QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) { : QMainWindow(parent), ui(new Ui::MainWindow), m_config(config) {
m_config = new Config(configDir, {}, this);
ui->setupUi(this); ui->setupUi(this);
_setupUI(); _setupUI();
} }
@@ -31,13 +28,25 @@ MainWindow::~MainWindow() {
} }
void MainWindow::_setupUI() { void MainWindow::_setupUI() {
// insert images carousel
m_carousel = new ImagesCarousel(
m_config.getStyleAspectRatio(),
m_config.getStyleImageWidth(),
m_config.getStyleImageFocusWidth(),
this);
ui->mainLayout->insertWidget(0, m_carousel);
// set window size
setMinimumSize(m_config.getStyleWindowWidth(), m_config.getStyleWindowHeight());
setMaximumSize(m_config.getStyleWindowWidth(), m_config.getStyleWindowHeight());
connect(ui->confirmButton, &QPushButton::clicked, this, &MainWindow::onConfirm); connect(ui->confirmButton, &QPushButton::clicked, this, &MainWindow::onConfirm);
connect(ui->cancelButton, &QPushButton::clicked, this, &MainWindow::onCancel); connect(ui->cancelButton, &QPushButton::clicked, this, &MainWindow::onCancel);
ui->confirmButton->setFocusPolicy(Qt::NoFocus); ui->confirmButton->setFocusPolicy(Qt::NoFocus);
ui->cancelButton->setFocusPolicy(Qt::NoFocus); ui->cancelButton->setFocusPolicy(Qt::NoFocus);
for (const auto &image : m_config->getWallpapers()) { for (const auto &image : m_config.getWallpapers()) {
ui->carousel->appendImage(image); m_carousel->appendImage(image);
} }
} }
@@ -47,9 +56,9 @@ void MainWindow::keyPressEvent(QKeyEvent *event) {
} else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { } else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
onConfirm(); onConfirm();
} else if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Tab || event->key() == Qt::Key_Right) { } else if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Tab || event->key() == Qt::Key_Right) {
ui->carousel->focusNextImage(); m_carousel->focusNextImage();
} else if (event->key() == Qt::Key_Left) { } else if (event->key() == Qt::Key_Left) {
ui->carousel->focusPrevImage(); m_carousel->focusPrevImage();
} else { } else {
QMainWindow::keyPressEvent(event); QMainWindow::keyPressEvent(event);
} }
@@ -57,13 +66,13 @@ void MainWindow::keyPressEvent(QKeyEvent *event) {
void MainWindow::onConfirm() { void MainWindow::onConfirm() {
close(); close();
const auto path = ui->carousel->getCurrentImagePath(); const auto path = m_carousel->getCurrentImagePath();
if (path.isEmpty()) { if (path.isEmpty()) {
warn("No image selected"); warn("No image selected");
return; return;
} }
info(QString("Selected image: %1").arg(path)); info(QString("Selected image: %1").arg(path));
const auto cmdOrig = m_config->getActionsConfirm(); const auto cmdOrig = m_config.getActionsConfirm();
if (cmdOrig.isEmpty()) { if (cmdOrig.isEmpty()) {
warn("No action defined for confirmation"); warn("No action defined for confirmation");
return; return;
+5 -3
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 00:37:58 * @Date: 2025-08-05 00:37:58
* @LastEditTime: 2025-08-05 17:23:41 * @LastEditTime: 2025-08-05 19:53:51
* @Description: MainWindow implementation. * @Description: MainWindow implementation.
*/ */
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
@@ -10,6 +10,7 @@
#include <QMainWindow> #include <QMainWindow>
#include "config.h" #include "config.h"
#include "images_carousel.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@@ -23,7 +24,7 @@ class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
MainWindow(const QString &configDir, QWidget *parent = nullptr); MainWindow(const Config &config, QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
public slots: public slots:
@@ -38,6 +39,7 @@ class MainWindow : public QMainWindow {
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
Config *m_config; ImagesCarousel *m_carousel = nullptr;
const Config &m_config;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H