feat: configurable sizes
This commit is contained in:
@@ -14,5 +14,12 @@
|
||||
},
|
||||
"actions": {
|
||||
"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
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: Uyanide pywang0608@foxmail.com
|
||||
* @Date: 2025-08-05 01:34:52
|
||||
* @LastEditTime: 2025-08-05 17:26:33
|
||||
* @LastEditTime: 2025-08-05 20:11:11
|
||||
* @Description: Configuration manager.
|
||||
*/
|
||||
#include "config.h"
|
||||
@@ -19,22 +19,6 @@ using namespace GeneralLogger;
|
||||
|
||||
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) {
|
||||
QFile configFile(configPath);
|
||||
if (!configFile.open(QIODevice::ReadOnly)) {
|
||||
@@ -50,42 +34,117 @@ void Config::_loadConfig(const QString &configPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const auto parseJsonArray = [](const QJsonObject &obj, const QString &key, QStringList &list) {
|
||||
if (obj.contains(key) && obj[key].isArray()) {
|
||||
QJsonArray array = obj[key].toArray();
|
||||
for (const QJsonValue &value : array) {
|
||||
if (value.isString()) {
|
||||
list.append(::expandPath(value.toString()));
|
||||
const auto jsonObj = jsonDoc.object();
|
||||
|
||||
struct ConfigMapping {
|
||||
QString path;
|
||||
QString key;
|
||||
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();
|
||||
if (!jsonObj.contains("wallpaper") || !jsonObj["wallpaper"].isObject()) {
|
||||
warn("Key 'wallpaper' not fount or not an object in config");
|
||||
} else {
|
||||
const auto wallpaperObj = jsonObj.value("wallpaper").toObject();
|
||||
parseJsonArray(wallpaperObj, "paths", m_configItems.wallpaperPaths);
|
||||
parseJsonArray(wallpaperObj, "dirs", m_configItems.wallpaperDirs);
|
||||
parseJsonArray(wallpaperObj, "excludes", m_configItems.wallpaperExcludes);
|
||||
}
|
||||
std::vector<ConfigMapping>
|
||||
mappings = {
|
||||
{"wallpaper.paths", "paths", [this](const QJsonValue &val) {
|
||||
parseJsonArray(val, m_configItems.wallpaperPaths);
|
||||
}},
|
||||
{"wallpaper.dirs", "dirs", [this](const QJsonValue &val) {
|
||||
parseJsonArray(val, m_configItems.wallpaperDirs);
|
||||
}},
|
||||
{"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");
|
||||
} else {
|
||||
const auto actionsObj = jsonObj.value("actions").toObject();
|
||||
if (actionsObj.contains("confirm") && actionsObj["confirm"].isString()) {
|
||||
m_configItems.actionsConfirm = ::expandPath(actionsObj["confirm"].toString());
|
||||
info(QString("Action on confirm: %1").arg(m_configItems.actionsConfirm));
|
||||
} else {
|
||||
warn("Key 'confirm' not found or not a string in 'actions'");
|
||||
}
|
||||
// 统一解析
|
||||
for (const auto &mapping : mappings) {
|
||||
([&mapping, &jsonObj]() {
|
||||
auto pathParts = mapping.path.split('.');
|
||||
|
||||
QJsonObject currentObj = jsonObj;
|
||||
QJsonValue targetValue;
|
||||
|
||||
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() {
|
||||
m_wallpapers.clear();
|
||||
|
||||
|
||||
+17
-2
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: Uyanide pywang0608@foxmail.com
|
||||
* @Date: 2025-08-05 01:34:52
|
||||
* @LastEditTime: 2025-08-05 17:26:25
|
||||
* @LastEditTime: 2025-08-05 20:12:47
|
||||
* @Description: Configuration manager.
|
||||
*/
|
||||
#ifndef CONFIG_H
|
||||
@@ -27,6 +27,16 @@ class Config : public QObject {
|
||||
|
||||
[[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;
|
||||
|
||||
private:
|
||||
@@ -35,11 +45,16 @@ class Config : public QObject {
|
||||
void _loadWallpapers();
|
||||
|
||||
private:
|
||||
struct ConfigItems {
|
||||
struct _ConfigItems {
|
||||
QStringList wallpaperPaths;
|
||||
QStringList wallpaperDirs;
|
||||
QStringList wallpaperExcludes;
|
||||
QString actionsConfirm;
|
||||
double styleAspectRatio = 1.6;
|
||||
int styleImageWidth = 320;
|
||||
int styleImageFocusWidth = 480;
|
||||
int styleWindowWidth = 800;
|
||||
int styleWindowHeight = 600;
|
||||
} m_configItems;
|
||||
|
||||
QStringList m_wallpapers;
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
|
||||
@@ -35,17 +35,7 @@
|
||||
<set>QMainWindow::DockOption::AllowTabbedDocks|QMainWindow::DockOption::AnimatedDocks</set>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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>
|
||||
<layout class="QVBoxLayout" name="mainLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="actions" native="true">
|
||||
<property name="minimumSize">
|
||||
@@ -100,14 +90,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ImagesCarousel</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>images_carousel.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
+54
-53
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: Uyanide pywang0608@foxmail.com
|
||||
* @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.
|
||||
*/
|
||||
#include "images_carousel.h"
|
||||
@@ -21,20 +21,27 @@
|
||||
|
||||
using namespace GeneralLogger;
|
||||
|
||||
ImagesCarousel::ImagesCarousel(QWidget* parent)
|
||||
ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
|
||||
const int itemWidth,
|
||||
const int itemFocusWidth,
|
||||
QWidget* parent)
|
||||
: QWidget(parent),
|
||||
ui(new Ui::ImagesCarousel),
|
||||
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);
|
||||
|
||||
connect(m_updateTimer, &QTimer::timeout, this, &ImagesCarousel::updateImages);
|
||||
connect(m_updateTimer, &QTimer::timeout, this, &ImagesCarousel::_updateImages);
|
||||
m_updateTimer->start(100);
|
||||
}
|
||||
|
||||
ImagesCarousel::~ImagesCarousel() {
|
||||
delete ui;
|
||||
for (auto item : m_imageQueue) {
|
||||
for (auto item : std::as_const(m_imageQueue)) {
|
||||
delete item;
|
||||
}
|
||||
if (m_scrollAnimation) {
|
||||
@@ -49,22 +56,26 @@ void ImagesCarousel::appendImage(const QString& path) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ImagesCarousel::addImageToQueue(const ImageData* data) {
|
||||
void ImagesCarousel::_addImageToQueue(const ImageData* data) {
|
||||
QMutexLocker locker(&m_queueMutex);
|
||||
auto imageItem = new ImageItem(data,
|
||||
s_itemWidth,
|
||||
s_itemHeight,
|
||||
s_itemFocusWidth,
|
||||
s_itemFocusHeight,
|
||||
this);
|
||||
auto imageItem = new ImageItem(
|
||||
data,
|
||||
m_itemWidth,
|
||||
m_itemHeight,
|
||||
m_itemFocusWidth,
|
||||
m_itemFocusHeight,
|
||||
this);
|
||||
m_imageQueue.enqueue(imageItem);
|
||||
}
|
||||
|
||||
void ImagesCarousel::updateImages() {
|
||||
void ImagesCarousel::_updateImages() {
|
||||
QMutexLocker locker(&m_queueMutex);
|
||||
|
||||
int processCount = 0;
|
||||
@@ -72,30 +83,31 @@ void ImagesCarousel::updateImages() {
|
||||
ImageItem* item = m_imageQueue.dequeue();
|
||||
ui->scrollAreaWidgetContents->layout()->addWidget(item);
|
||||
m_loadedImages.append(item);
|
||||
// focus first image
|
||||
if (m_loadedImages.size() == 1) {
|
||||
item->focusImage();
|
||||
item->setFocus(true);
|
||||
} else {
|
||||
item->unfocusImage();
|
||||
item->setFocus(false);
|
||||
}
|
||||
processCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageLoader::run() {
|
||||
auto data = new ImageData(m_path);
|
||||
auto data = new ImageData(m_path, m_initWidth, m_initHeight);
|
||||
QMetaObject::invokeMethod(m_carousel,
|
||||
"addImageToQueue",
|
||||
"_addImageToQueue",
|
||||
Qt::QueuedConnection,
|
||||
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;
|
||||
if (!pixmap.load(p)) {
|
||||
warn(QString("Failed to load image from path: %1").arg(p));
|
||||
}
|
||||
// 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);
|
||||
|
||||
// Crop to center
|
||||
@@ -105,34 +117,34 @@ ImageData::ImageData(const QString& p) : path(p) {
|
||||
}
|
||||
|
||||
void ImagesCarousel::focusNextImage() {
|
||||
unfocusCurrImage();
|
||||
_unfocusCurrImage();
|
||||
if (m_loadedImages.size() <= 1) return;
|
||||
m_currentIndex++;
|
||||
if (m_currentIndex >= m_loadedImages.size()) {
|
||||
m_currentIndex = 0;
|
||||
}
|
||||
focusCurrImage();
|
||||
_focusCurrImage();
|
||||
}
|
||||
|
||||
void ImagesCarousel::focusPrevImage() {
|
||||
if (m_loadedImages.size() <= 1) return;
|
||||
unfocusCurrImage();
|
||||
_unfocusCurrImage();
|
||||
m_currentIndex--;
|
||||
if (m_currentIndex < 0) {
|
||||
m_currentIndex = m_loadedImages.size() - 1;
|
||||
}
|
||||
focusCurrImage();
|
||||
_focusCurrImage();
|
||||
}
|
||||
|
||||
void ImagesCarousel::unfocusCurrImage() {
|
||||
m_loadedImages[m_currentIndex]->unfocusImage();
|
||||
void ImagesCarousel::_unfocusCurrImage() {
|
||||
m_loadedImages[m_currentIndex]->setFocus(false);
|
||||
}
|
||||
|
||||
void ImagesCarousel::focusCurrImage() {
|
||||
m_loadedImages[m_currentIndex]->focusImage();
|
||||
void ImagesCarousel::_focusCurrImage() {
|
||||
m_loadedImages[m_currentIndex]->setFocus(true);
|
||||
auto hScrollBar = ui->scrollArea->horizontalScrollBar();
|
||||
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;
|
||||
if (leftOffset < 0) {
|
||||
leftOffset = 0;
|
||||
@@ -162,12 +174,21 @@ ImageItem::ImageItem(const ImageData* data,
|
||||
m_data(data),
|
||||
m_itemSize(itemWidth, itemHeight),
|
||||
m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
|
||||
setPixmap(data->pixmap);
|
||||
setFixedSize(ImagesCarousel::s_itemWidth, ImagesCarousel::s_itemHeight);
|
||||
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) {
|
||||
m_scaleAnimation->stop();
|
||||
delete m_scaleAnimation;
|
||||
@@ -176,7 +197,7 @@ void ImageItem::focusImage() {
|
||||
m_scaleAnimation = new QPropertyAnimation(this, "size");
|
||||
m_scaleAnimation->setDuration(ImagesCarousel::s_animationDuration);
|
||||
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,
|
||||
@@ -186,23 +207,3 @@ void ImageItem::focusImage() {
|
||||
});
|
||||
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);
|
||||
connect(m_scaleAnimation,
|
||||
&QPropertyAnimation::valueChanged,
|
||||
this,
|
||||
[this](const QVariant& value) {
|
||||
setFixedSize(value.toSize());
|
||||
});
|
||||
m_scaleAnimation->start();
|
||||
}
|
||||
+37
-17
@@ -1,12 +1,14 @@
|
||||
/*
|
||||
* @Author: Uyanide pywang0608@foxmail.com
|
||||
* @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.
|
||||
*/
|
||||
#ifndef IMAGES_CAROUSEL_H
|
||||
#define IMAGES_CAROUSEL_H
|
||||
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QMutex>
|
||||
@@ -22,15 +24,20 @@
|
||||
|
||||
class ImagesCarousel;
|
||||
|
||||
/**
|
||||
* @brief Data structure to hold image information
|
||||
* and can be safely created and passed between threads.
|
||||
*/
|
||||
struct ImageData {
|
||||
QString path;
|
||||
QPixmap pixmap;
|
||||
|
||||
ImageData() = default;
|
||||
|
||||
explicit ImageData(const QString& p);
|
||||
explicit ImageData(const QString& p, const int initWidth, const int initHeight);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Image label that displays an image
|
||||
*/
|
||||
class ImageItem : public QLabel {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -42,13 +49,14 @@ class ImageItem : public QLabel {
|
||||
const int itemFocusHeight,
|
||||
QWidget* parent = nullptr);
|
||||
|
||||
~ImageItem() override;
|
||||
|
||||
[[nodiscard]] const QString& getPath() const { return m_data->path; }
|
||||
|
||||
[[nodiscard]] const QPixmap& getPixmap() const { return m_data->pixmap; }
|
||||
|
||||
public slots:
|
||||
void focusImage();
|
||||
void unfocusImage();
|
||||
public:
|
||||
void setFocus(bool focus = true);
|
||||
|
||||
private:
|
||||
const ImageData* m_data;
|
||||
@@ -65,6 +73,8 @@ class ImageLoader : public QRunnable {
|
||||
private:
|
||||
QString m_path;
|
||||
ImagesCarousel* m_carousel;
|
||||
const int m_initWidth;
|
||||
const int m_initHeight;
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
@@ -74,14 +84,15 @@ class ImagesCarousel;
|
||||
class ImagesCarousel : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
friend void ImageLoader::run();
|
||||
|
||||
public:
|
||||
explicit ImagesCarousel(QWidget* parent = nullptr);
|
||||
explicit ImagesCarousel(const double itemAspectRatio,
|
||||
const int itemWidth,
|
||||
const int itemFocusWidth,
|
||||
QWidget* parent = nullptr);
|
||||
~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;
|
||||
|
||||
[[nodiscard]] QString getCurrentImagePath() const {
|
||||
@@ -91,16 +102,25 @@ class ImagesCarousel : public QWidget {
|
||||
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:
|
||||
void addImageToQueue(const ImageData* data);
|
||||
void appendImage(const QString& path);
|
||||
void focusNextImage();
|
||||
void focusPrevImage();
|
||||
void unfocusCurrImage();
|
||||
void focusCurrImage();
|
||||
|
||||
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:
|
||||
Ui::ImagesCarousel* ui;
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: Uyanide pywang0608@foxmail.com
|
||||
* @Date: 2025-08-05 00:37:58
|
||||
* @LastEditTime: 2025-08-05 17:34:37
|
||||
* @LastEditTime: 2025-08-05 19:42:07
|
||||
* @Description: Entry point.
|
||||
*/
|
||||
#include <qapplication.h>
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "config.h"
|
||||
#include "logger.h"
|
||||
#include "main_window.h"
|
||||
|
||||
@@ -28,7 +29,9 @@ static QString getConfigDir() {
|
||||
int main(int argc, char *argv[]) {
|
||||
QApplication a(argc, argv);
|
||||
|
||||
MainWindow w(::getConfigDir());
|
||||
Config config(getConfigDir());
|
||||
|
||||
MainWindow w(config);
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
|
||||
+21
-12
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: Uyanide pywang0608@foxmail.com
|
||||
* @Date: 2025-08-05 00:37:58
|
||||
* @LastEditTime: 2025-08-05 17:40:35
|
||||
* @LastEditTime: 2025-08-05 20:12:40
|
||||
* @Description: MainWindow implementation.
|
||||
*/
|
||||
#include "main_window.h"
|
||||
@@ -17,11 +17,8 @@
|
||||
|
||||
using namespace GeneralLogger;
|
||||
|
||||
MainWindow::MainWindow(const QString &configDir, QWidget *parent)
|
||||
: QMainWindow(parent), ui(new Ui::MainWindow) {
|
||||
|
||||
m_config = new Config(configDir, {}, this);
|
||||
|
||||
MainWindow::MainWindow(const Config &config, QWidget *parent)
|
||||
: QMainWindow(parent), ui(new Ui::MainWindow), m_config(config) {
|
||||
ui->setupUi(this);
|
||||
_setupUI();
|
||||
}
|
||||
@@ -31,13 +28,25 @@ MainWindow::~MainWindow() {
|
||||
}
|
||||
|
||||
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->cancelButton, &QPushButton::clicked, this, &MainWindow::onCancel);
|
||||
ui->confirmButton->setFocusPolicy(Qt::NoFocus);
|
||||
ui->cancelButton->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
for (const auto &image : m_config->getWallpapers()) {
|
||||
ui->carousel->appendImage(image);
|
||||
for (const auto &image : m_config.getWallpapers()) {
|
||||
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) {
|
||||
onConfirm();
|
||||
} 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) {
|
||||
ui->carousel->focusPrevImage();
|
||||
m_carousel->focusPrevImage();
|
||||
} else {
|
||||
QMainWindow::keyPressEvent(event);
|
||||
}
|
||||
@@ -57,13 +66,13 @@ void MainWindow::keyPressEvent(QKeyEvent *event) {
|
||||
|
||||
void MainWindow::onConfirm() {
|
||||
close();
|
||||
const auto path = ui->carousel->getCurrentImagePath();
|
||||
const auto path = m_carousel->getCurrentImagePath();
|
||||
if (path.isEmpty()) {
|
||||
warn("No image selected");
|
||||
return;
|
||||
}
|
||||
info(QString("Selected image: %1").arg(path));
|
||||
const auto cmdOrig = m_config->getActionsConfirm();
|
||||
const auto cmdOrig = m_config.getActionsConfirm();
|
||||
if (cmdOrig.isEmpty()) {
|
||||
warn("No action defined for confirmation");
|
||||
return;
|
||||
|
||||
+5
-3
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: Uyanide pywang0608@foxmail.com
|
||||
* @Date: 2025-08-05 00:37:58
|
||||
* @LastEditTime: 2025-08-05 17:23:41
|
||||
* @LastEditTime: 2025-08-05 19:53:51
|
||||
* @Description: MainWindow implementation.
|
||||
*/
|
||||
#ifndef MAINWINDOW_H
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "config.h"
|
||||
#include "images_carousel.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -23,7 +24,7 @@ class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow(const QString &configDir, QWidget *parent = nullptr);
|
||||
MainWindow(const Config &config, QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
public slots:
|
||||
@@ -38,6 +39,7 @@ class MainWindow : public QMainWindow {
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
Config *m_config;
|
||||
ImagesCarousel *m_carousel = nullptr;
|
||||
const Config &m_config;
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
||||
|
||||
Reference in New Issue
Block a user