diff --git a/README.md b/README.md index 9e45cf4..1d08868 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ It might not be that worthy to write a QtWidget application for such a small fea > [!Warning] > -> This script will ask for `sudo` permission if the prefix is set to a system directory like `/usr/local`. Please make sure you have read and trust the script before proceeding. +> This script will ask for root permissions if the prefix is set to a system directory like `/usr/local`. Please make sure you have read and trust the script before proceeding. ## How to use @@ -65,3 +65,31 @@ By default, the path of the selected wallpaper will be output to stdout. If you ``` `action.confirm` should be a executable followed by a couple of arguments, where `%1` will be replaced by the path of the selected wallpaper. + +## CLI + +``` +Usage: wallpaper-carousel [options] + +Options: + -h, --help Displays help on commandline options. + -v, --version Displays version information. + -V, --verbose Set log level to DEBUG (default is INFO) + -q, --quiet Suppress all log output + -d, --append-dir Append an additional wallpaper search directory + -c, --config-file Specify a custom configuration file +``` + +A few things to notice: + +- It's generally not necessary to provide any CLI arguments, I would recommend using the config file to customize the behavior instead. However, it is still possible to control some essential options via CLI. + +- All logs are directed to stderr by default. Only the full path of the selected wallpaper (if any) will be sent to stdout. This allows easy piping of the output to other programs. + +- The `--append-dir` option can be used multiple times to add multiple directories. + +- It is quite obvious that some options are conflicting with each other (e.g. `--verbose` and `--quiet`). If mutually exclusive options are provided together, the behavior is undefined and can be changed without notice in future versions. + +- Paths passed via CLI options are tested before any further operation is performed. That is to say, if an invalid path is provided, the program will exit with an error before any further action, and you won't even have a chance to see a window. + + On the contrary, paths provided in the config file are only tested when they are actually used (e.g. when searching for wallpapers). And most errors will be ignored silently (with a warning log). diff --git a/src/image_item.cpp b/src/image_item.cpp index fb8e147..ce438e4 100644 --- a/src/image_item.cpp +++ b/src/image_item.cpp @@ -1,7 +1,7 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-11-30 20:32:27 - * @LastEditTime: 2026-01-15 07:09:29 + * @LastEditTime: 2026-02-07 07:13:49 * @Description: Image item widget for displaying an image. */ #include "image_item.h" @@ -14,10 +14,10 @@ using namespace GeneralLogger; ImageData* ImageData::create(const QString& p, const int initWidth, const int initHeight) { ImageData* data = new ImageData(p); - data->image = new QImage(); // Use QImageReader for better performance QImageReader reader(p); + reader.setAutoTransform(true); if (!reader.canRead()) { warn(QString("Failed to load image from path: %1").arg(p)); delete data; @@ -28,7 +28,7 @@ ImageData* ImageData::create(const QString& p, const int initWidth, const int in const QSize originalSize = reader.size(); // Scale the image to fit the target size while maintaining aspect ratio - if (originalSize.isValid()) { + if (originalSize.isValid() && reader.supportsOption(QImageIOHandler::ScaledSize)) { double widthRatio = (double)targetSize.width() / originalSize.width(); double heightRatio = (double)targetSize.height() / originalSize.height(); double scaleFactor = std::max(widthRatio, heightRatio); @@ -37,19 +37,22 @@ ImageData* ImageData::create(const QString& p, const int initWidth, const int in reader.setScaledSize(scaledSize); } - if (!reader.read(data->image)) { + QImage tempImage; + if (!reader.read(&tempImage)) { warn(QString("Failed to load image from path: %1").arg(p)); delete data; return nullptr; } // Crop to target size if necessary - if (data->image->size() != targetSize) { - int x = (data->image->width() - targetSize.width()) / 2; - int y = (data->image->height() - targetSize.height()) / 2; - *data->image = data->image->copy(x, y, targetSize.width(), targetSize.height()); + if (tempImage.size() != targetSize) { + int x = (tempImage.width() - targetSize.width()) / 2; + int y = (tempImage.height() - targetSize.height()) / 2; + tempImage = tempImage.copy(x, y, targetSize.width(), targetSize.height()); } + data->image = new QImage(std::move(tempImage)); + return data; } diff --git a/src/images_carousel.h b/src/images_carousel.h index 4b8d61d..b2ee638 100644 --- a/src/images_carousel.h +++ b/src/images_carousel.h @@ -1,7 +1,7 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 01:22:53 - * @LastEditTime: 2026-01-15 07:43:44 + * @LastEditTime: 2026-01-18 06:39:44 * @Description: Animated carousel widget for displaying and selecting images. */ #ifndef IMAGES_CAROUSEL_H @@ -24,13 +24,13 @@ Two different image loading strategies: - With loading screen: load all images directly - 1. appendImages called -> increace m_addedImagesCount & spawn all ImageLoader - threads + 1. appendImages called -> increace m_addedImagesCount & disable UI updates & + spawn and start all ImageLoader threads 2. Each ImageLoader calls _insertImageQueue with queued connection 3. _insertImageQueue calls _insertImage directly - Without loading screen: queue loaded images and insert them in batches - 1. appendImages called -> increace m_addedImagesCount & spawn all ImageLoader - threads and a timer m_imageInsertQueueTimer, disable UI updates + 1. appendImages called -> increace m_addedImagesCount & spawn and start all + ImageLoader threads and a timer m_imageInsertQueueTimer & disable animations 2. Each ImageLoader calls _insertImageQueue with queued connection 3. _insertImageQueue enqueues the ImageData 4. m_imageInsertQueueTimer calls _processImageInsertQueue every @@ -38,7 +38,7 @@ Two different image loading strategies: 5. _processImageInsertQueue processes up to s_processBatchSize items from the queue and calls _insertImage for each -The stop logic is identical: +The stop logic is identical regardless of whether loading screen is used or not: - Force stop 1. Set m_stopSign to true 2. ImageLoader::run checks m_stopSign and returns early if true diff --git a/src/main.cpp b/src/main.cpp index fb136d1..53744ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-08-05 00:37:58 - * @LastEditTime: 2026-01-15 07:51:54 + * @LastEditTime: 2026-01-15 14:11:06 * @Description: Argument parser and entry point. */ #include @@ -71,7 +71,7 @@ static class AppOptions { const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption versionOption = parser.addVersionOption(); - QCommandLineOption verboseOption(QStringList() << "V" << "verbose", "Set log level to DEBUG"); + 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"); diff --git a/src/utils.h b/src/utils.h index 1074883..fa7dffe 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,7 +1,7 @@ /* * @Author: Uyanide pywang0608@foxmail.com * @Date: 2025-11-30 20:59:57 - * @LastEditTime: 2026-01-15 09:55:26 + * @LastEditTime: 2026-01-18 06:36:13 * @Description: THE utils header that every project needs :) */ @@ -110,19 +110,6 @@ static QString splitNameFromPath(const QString& path) { * @return false */ inline bool checkImageFile(const QString& filePath) { - static const QStringList validExtensions = { - ".jpg", - ".jpeg", - ".jfif", - ".png", - ".bmp", - ".gif", - ".webp", - ".tiff", - ".avif", - ".heic", - ".heif"}; - // check if exist if (!QFile::exists(filePath)) { return false;