diff --git a/CMakeLists.txt b/CMakeLists.txt index dc0979e..af9d2e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC_SEARCH_PATHS src/designer) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -14,19 +16,19 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) set(PROJECT_SOURCES - main.cpp - mainwindow.cpp - mainwindow.h - mainwindow.ui + src/main.cpp + src/main_window.cpp + src/main_window.h + src/designer/main_window.ui ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(wallpaper_chooser MANUAL_FINALIZATION ${PROJECT_SOURCES} - images_carousel.h images_carousel.cpp images_carousel.ui - config.h config.cpp - logger.h + src/images_carousel.h src/images_carousel.cpp src/designer/images_carousel.ui + src/config.h src/config.cpp + src/logger.h ) # Define target properties for Android with Qt 6 as: # set_property(TARGET wallpaper_chooser APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR @@ -48,7 +50,7 @@ endif() target_link_libraries(wallpaper_chooser PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) -target_include_directories(wallpaper_chooser PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_include_directories(wallpaper_chooser PRIVATE src) # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..77fa039 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2025 Uyanide pywang0608@foxmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/config.example.json b/config.example.json index 8ef0dd2..cde3c83 100644 --- a/config.example.json +++ b/config.example.json @@ -1,17 +1,18 @@ { - "wallpaper": { - "paths": [ - "~/Pictures/116327446_p0.jpg" - ], - "dirs": [ - "~/.config/backgrounds" - ], - "excludes": [ - "~/.config/backgrounds/nao-start-crop-adjusted.jpg", - "~/.config/backgrounds/README.md" - ] - }, - "actions": { - "confirm": "~/.scripts/change_wallpaper.sh " - } + "wallpaper": { + "paths": [ + "~/Pictures/116327446_p0.jpg" + ], + "dirs": [ + "~/.config/backgrounds" + ], + "excludes": [ + "~/.config/backgrounds/nao-start-crop-adjusted.jpg", + "~/.config/backgrounds/miku-gate.jpg", + "~/.config/backgrounds/README.md" + ] + }, + "actions": { + "confirm": "~/.scripts/change-wallpaper.fish \"%1\"" + } } \ No newline at end of file diff --git a/config.cpp b/src/config.cpp similarity index 84% rename from config.cpp rename to src/config.cpp index 000b326..41119b0 100644 --- a/config.cpp +++ b/src/config.cpp @@ -1,8 +1,8 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 01:34:52 - * @LastEditTime: 2025-08-05 12:17:37 - * @Description: + * @LastEditTime: 2025-08-05 17:26:33 + * @Description: Configuration manager. */ #include "config.h" @@ -66,12 +66,24 @@ void Config::_loadConfig(const QString &configPath) { const auto jsonObj = jsonDoc.object(); if (!jsonObj.contains("wallpaper") || !jsonObj["wallpaper"].isObject()) { warn("Key 'wallpaper' not fount or not an object in config"); - return; + } 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); + } + + 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'"); + } } - 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); } void Config::_loadWallpapers() { diff --git a/config.h b/src/config.h similarity index 81% rename from config.h rename to src/config.h index e30e890..caeb4fe 100644 --- a/config.h +++ b/src/config.h @@ -1,15 +1,12 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 01:34:52 - * @LastEditTime: 2025-08-05 12:14:04 - * @Description: + * @LastEditTime: 2025-08-05 17:26:25 + * @Description: Configuration manager. */ #ifndef CONFIG_H #define CONFIG_H -#include -#include - #include #include #include @@ -28,6 +25,8 @@ class Config : public QObject { [[nodiscard]] qint64 getWallpaperCount() const { return m_wallpapers.size(); } + [[nodiscard]] const QString& getActionsConfirm() const { return m_configItems.actionsConfirm; } + static const QString s_DefaultConfigFileName; private: @@ -40,6 +39,7 @@ class Config : public QObject { QStringList wallpaperPaths; QStringList wallpaperDirs; QStringList wallpaperExcludes; + QString actionsConfirm; } m_configItems; QStringList m_wallpapers; diff --git a/images_carousel.ui b/src/designer/images_carousel.ui similarity index 100% rename from images_carousel.ui rename to src/designer/images_carousel.ui diff --git a/mainwindow.ui b/src/designer/main_window.ui similarity index 100% rename from mainwindow.ui rename to src/designer/main_window.ui diff --git a/images_carousel.cpp b/src/images_carousel.cpp similarity index 98% rename from images_carousel.cpp rename to src/images_carousel.cpp index c59dfb7..dbaac52 100644 --- a/images_carousel.cpp +++ b/src/images_carousel.cpp @@ -1,8 +1,8 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 01:22:53 - * @LastEditTime: 2025-08-05 16:51:12 - * @Description: + * @LastEditTime: 2025-08-05 17:25:59 + * @Description: Animated carousel widget for displaying and selecting images. */ #include "images_carousel.h" diff --git a/images_carousel.h b/src/images_carousel.h similarity index 84% rename from images_carousel.h rename to src/images_carousel.h index 4f0d8f6..3d8eedb 100644 --- a/images_carousel.h +++ b/src/images_carousel.h @@ -1,8 +1,8 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 01:22:53 - * @LastEditTime: 2025-08-05 16:49:22 - * @Description: + * @LastEditTime: 2025-08-05 17:25:34 + * @Description: Animated carousel widget for displaying and selecting images. */ #ifndef IMAGES_CAROUSEL_H #define IMAGES_CAROUSEL_H @@ -42,6 +42,10 @@ class ImageItem : public QLabel { const int itemFocusHeight, QWidget* parent = nullptr); + [[nodiscard]] const QString& getPath() const { return m_data->path; } + + [[nodiscard]] const QPixmap& getPixmap() const { return m_data->pixmap; } + public slots: void focusImage(); void unfocusImage(); @@ -80,6 +84,13 @@ class ImagesCarousel : public QWidget { static constexpr int s_itemFocusHeight = 300; static constexpr int s_animationDuration = 300; + [[nodiscard]] QString getCurrentImagePath() const { + if (m_currentIndex < 0 || m_currentIndex >= m_loadedImages.size()) { + return ""; + } + return m_loadedImages[m_currentIndex]->getPath(); + } + public slots: void addImageToQueue(const ImageData* data); void appendImage(const QString& path); @@ -99,9 +110,6 @@ class ImagesCarousel : public QWidget { QTimer* m_updateTimer; int m_currentIndex = 0; QPropertyAnimation* m_scrollAnimation; - - signals: - void imageLoaded(ImageData* imageData); }; class ImagesCarouselScrollArea : public QScrollArea { diff --git a/logger.h b/src/logger.h similarity index 91% rename from logger.h rename to src/logger.h index 44c500a..8feb998 100644 --- a/logger.h +++ b/src/logger.h @@ -1,8 +1,9 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 10:43:31 - * @LastEditTime: 2025-08-05 11:49:27 - * @Description: + * @LastEditTime: 2025-08-05 17:25:18 + * @Description: A simple logger for general use. + * g_logStream must be defined somewhere else in the project. */ #ifndef GENERAL_LOGGER_H #define GENERAL_LOGGER_H diff --git a/main.cpp b/src/main.cpp similarity index 88% rename from main.cpp rename to src/main.cpp index de2b809..37e4780 100644 --- a/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 00:37:58 - * @LastEditTime: 2025-08-05 12:16:35 - * @Description: + * @LastEditTime: 2025-08-05 17:34:37 + * @Description: Entry point. */ #include @@ -12,7 +12,7 @@ #include #include "logger.h" -#include "mainwindow.h" +#include "main_window.h" QTextStream GeneralLogger::g_logStream(stderr); diff --git a/mainwindow.cpp b/src/main_window.cpp similarity index 61% rename from mainwindow.cpp rename to src/main_window.cpp index 8f0ae53..81d6980 100644 --- a/mainwindow.cpp +++ b/src/main_window.cpp @@ -1,16 +1,21 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 00:37:58 - * @LastEditTime: 2025-08-05 16:51:52 - * @Description: + * @LastEditTime: 2025-08-05 17:40:35 + * @Description: MainWindow implementation. */ -#include "mainwindow.h" +#include "main_window.h" #include #include +#include #include -#include "./ui_mainwindow.h" +#include "./ui_main_window.h" +#include "images_carousel.h" +#include "logger.h" + +using namespace GeneralLogger; MainWindow::MainWindow(const QString &configDir, QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { @@ -52,6 +57,26 @@ void MainWindow::keyPressEvent(QKeyEvent *event) { void MainWindow::onConfirm() { close(); + const auto path = ui->carousel->getCurrentImagePath(); + if (path.isEmpty()) { + warn("No image selected"); + return; + } + info(QString("Selected image: %1").arg(path)); + const auto cmdOrig = m_config->getActionsConfirm(); + if (cmdOrig.isEmpty()) { + warn("No action defined for confirmation"); + return; + } + const auto cmd = cmdOrig.arg(path); + info(QString("Executing command: %1").arg(cmd)); + + const auto arguments = QProcess::splitCommand(cmd); + + if (QProcess::execute(arguments.first(), arguments.mid(1))) { + error(QString("Failed to execute command: %1").arg(cmd)); + return; + } } void MainWindow::onCancel() { diff --git a/mainwindow.h b/src/main_window.h similarity index 88% rename from mainwindow.h rename to src/main_window.h index 91fbf29..46f5a96 100644 --- a/mainwindow.h +++ b/src/main_window.h @@ -1,8 +1,8 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 00:37:58 - * @LastEditTime: 2025-08-05 12:01:25 - * @Description: + * @LastEditTime: 2025-08-05 17:23:41 + * @Description: MainWindow implementation. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H