2 Commits

Author SHA1 Message Date
Uyanide d39e36e096 feat: add reload button to force 'reload from disk'
Release / Build ArchLinux Package (push) Successful in 1m3s
Release / Publish to Gitea Release (push) Successful in 4s
Release / Publish to AUR (push) Successful in 9s
2026-04-05 19:22:12 +02:00
Uyanide cf73b12996 feat: quitOnSelected default to true
Release / Build ArchLinux Package (push) Successful in 1m0s
Release / Publish to Gitea Release (push) Successful in 3s
Release / Publish to AUR (push) Successful in 9s
2026-04-03 09:09:40 +02:00
16 changed files with 78 additions and 14 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(WallReel VERSION 2.1.0 LANGUAGES CXX) project(WallReel VERSION 2.2.0 LANGUAGES CXX)
set(EXECUTABLE_NAME "wallreel") set(EXECUTABLE_NAME "wallreel")
set(CORELIB_NAME "wallreel-core") set(CORELIB_NAME "wallreel-core")
+1 -1
View File
@@ -121,7 +121,7 @@ Configures system commands to execute on specific events mapping to your window
| `onPreview` | String | `""` | Command to execute when a wallpaper is previewed. | | `onPreview` | String | `""` | Command to execute when a wallpaper is previewed. |
| `saveState` | Array of Objects | `[]` | Commands to fetch system states before changing wallpapers. Each object defines: `key`, `fallback` (fallback value), `command` (stdout mapping), and `timeout` (ms). | | `saveState` | Array of Objects | `[]` | Commands to fetch system states before changing wallpapers. Each object defines: `key`, `fallback` (fallback value), `command` (stdout mapping), and `timeout` (ms). |
| `onRestore` | String | `""` | Command to execute on restore. Extracted states from `saveState` can be injected using `{{ key }}`. | | `onRestore` | String | `""` | Command to execute on restore. Extracted states from `saveState` can be injected using `{{ key }}`. |
| `quitOnSelected` | Boolean | `false` | Quit the application after a selection is made. | | `quitOnSelected` | Boolean | `true` | Quit the application after a selection is made. |
| `restoreOnClose` | Boolean | `true` | Run `onRestore` command if the application is closed without making a final selection. | | `restoreOnClose` | Boolean | `true` | Run `onRestore` command if the application is closed without making a final selection. |
Available placeholders for `onSelected`, `onPreview` commands: Available placeholders for `onSelected`, `onPreview` commands:
+1 -1
View File
@@ -97,7 +97,7 @@ Each item has:
executed on restore. executed on restore.
Saved state keys are usable as placeholders. Saved state keys are usable as placeholders.
.PP .PP
\f[CR]quitOnSelected\f[R] (boolean, default: \f[CR]false\f[R]) : Exit \f[CR]quitOnSelected\f[R] (boolean, default: \f[CR]true\f[R]) : Exit
application immediately after confirming a selection. application immediately after confirming a selection.
.PP .PP
\f[CR]restoreOnClose\f[R] (boolean, default: \f[CR]true\f[R]) : Run \f[CR]restoreOnClose\f[R] (boolean, default: \f[CR]true\f[R]) : Run
+2 -2
View File
@@ -35,7 +35,7 @@
// action.saveState[].command string "" Command that outputs(to stdout) the value to save when executed // action.saveState[].command string "" Command that outputs(to stdout) the value to save when executed
// action.saveState[].timeout number 3000 Timeout for executing "command" in milliseconds. 0 or negative means no timeout // action.saveState[].timeout number 3000 Timeout for executing "command" in milliseconds. 0 or negative means no timeout
// action.onRestore string "" Command to execute on restore ({{ key }} -> value defined or obtained in saveState) // action.onRestore string "" Command to execute on restore ({{ key }} -> value defined or obtained in saveState)
// action.quitOnSelected boolean false Whether to quit the application after confirming a wallpaper // action.quitOnSelected boolean true Whether to quit the application after confirming a wallpaper
// action.restoreOnClose boolean true Whether to run the restore command after closing the application without confirming a wallpaper // action.restoreOnClose boolean true Whether to run the restore command after closing the application without confirming a wallpaper
// //
// style.image_width number 320 Width of each image // style.image_width number 320 Width of each image
@@ -125,7 +125,7 @@ struct ActionConfigItems {
QString onRestore; QString onRestore;
int previewDebounceTime = 300; // milliseconds int previewDebounceTime = 300; // milliseconds
bool printSelected = true; bool printSelected = true;
bool quitOnSelected = false; bool quitOnSelected = true;
bool restoreOnClose = true; bool restoreOnClose = true;
}; };
+1 -4
View File
@@ -54,9 +54,6 @@ Manager::Manager(
WR_INFO(QString("No search directories specified, using Pictures directory: %1").arg(picturesPath)); WR_INFO(QString("No search directories specified, using Pictures directory: %1").arg(picturesPath));
m_wallpaperConfig.dirs.append({picturesPath, true}); m_wallpaperConfig.dirs.append({picturesPath, true});
} }
WR_DEBUG("Loading wallpapers ...");
_loadWallpapers();
} }
Manager::~Manager() { Manager::~Manager() {
@@ -324,7 +321,7 @@ void Manager::_loadCacheConfig(const QJsonObject& root) {
} }
} }
void Manager::_loadWallpapers() { void Manager::scanWallpapers() {
m_wallpapers.clear(); m_wallpapers.clear();
// Add paths first using a set to avoid duplicates // Add paths first using a set to avoid duplicates
+2 -2
View File
@@ -72,6 +72,8 @@ class Manager : public QObject {
*/ */
Q_INVOKABLE void captureState(); Q_INVOKABLE void captureState();
void scanWallpapers();
signals: signals:
void stateCaptured(); void stateCaptured();
@@ -83,8 +85,6 @@ class Manager : public QObject {
void _loadActionConfig(const QJsonObject& config); void _loadActionConfig(const QJsonObject& config);
void _loadStyleConfig(const QJsonObject& config); void _loadStyleConfig(const QJsonObject& config);
void _loadCacheConfig(const QJsonObject& config); void _loadCacheConfig(const QJsonObject& config);
// Load wallpapers
void _loadWallpapers();
// Callback for state capture results // Callback for state capture results
void _onCaptureResult(const QString& key, const QString& value); void _onCaptureResult(const QString& key, const QString& value);
+22
View File
@@ -9,10 +9,12 @@
WALLREEL_DECLARE_SENDER("ImageManager") WALLREEL_DECLARE_SENDER("ImageManager")
WallReel::Core::Image::Manager::Manager( WallReel::Core::Image::Manager::Manager(
Config::Manager& configMgr,
Cache::Manager& cacheMgr, Cache::Manager& cacheMgr,
const QSize& thumbnailSize, const QSize& thumbnailSize,
QObject* parent) QObject* parent)
: QObject(parent), : QObject(parent),
m_configMgr(configMgr),
m_cacheMgr(cacheMgr), m_cacheMgr(cacheMgr),
m_thumbnailSize(thumbnailSize) { m_thumbnailSize(thumbnailSize) {
m_dataModel = new Model(this); m_dataModel = new Model(this);
@@ -38,6 +40,21 @@ WallReel::Core::Image::Manager::~Manager() {
m_watcher.waitForFinished(); m_watcher.waitForFinished();
} }
void WallReel::Core::Image::Manager::loadAndProcess() {
if (m_isLoading) {
WR_WARN("Already loading images. Ignoring new load request.");
return;
}
m_isLoading = true;
emit isLoadingChanged();
_clearData();
m_configMgr.scanWallpapers();
const auto paths = m_configMgr.getWallpapers();
return _process(paths);
}
void WallReel::Core::Image::Manager::loadAndProcess(const QStringList& paths) { void WallReel::Core::Image::Manager::loadAndProcess(const QStringList& paths) {
if (m_isLoading) { if (m_isLoading) {
WR_WARN("Already loading images. Ignoring new load request."); WR_WARN("Already loading images. Ignoring new load request.");
@@ -48,6 +65,10 @@ void WallReel::Core::Image::Manager::loadAndProcess(const QStringList& paths) {
_clearData(); _clearData();
return _process(paths);
}
void WallReel::Core::Image::Manager::_process(const QStringList& paths) {
m_processedCount = 0; m_processedCount = 0;
m_progressUpdateTimer.start(s_ProgressUpdateIntervalMs); m_progressUpdateTimer.start(s_ProgressUpdateIntervalMs);
// These are all small objects so capturing by value should be fine // These are all small objects so capturing by value should be fine
@@ -75,6 +96,7 @@ void WallReel::Core::Image::Manager::stop() {
void WallReel::Core::Image::Manager::_clearData() { void WallReel::Core::Image::Manager::_clearData() {
m_dataModel->clearData(); m_dataModel->clearData();
m_dataMap.clear();
} }
void WallReel::Core::Image::Manager::_onProgressValueChanged(int value) { void WallReel::Core::Image::Manager::_onProgressValueChanged(int value) {
+8
View File
@@ -8,6 +8,7 @@
#include <atomic> #include <atomic>
#include "Cache/manager.hpp" #include "Cache/manager.hpp"
#include "Config/manager.hpp"
#include "data.hpp" #include "data.hpp"
#include "model.hpp" #include "model.hpp"
@@ -20,6 +21,7 @@ class Manager : public QObject {
// Constructor / Destructor // Constructor / Destructor
Manager( Manager(
Config::Manager& configMgr,
Cache::Manager& cacheMgr, Cache::Manager& cacheMgr,
const QSize& thumbnailSize, const QSize& thumbnailSize,
QObject* parent = nullptr); QObject* parent = nullptr);
@@ -32,6 +34,8 @@ class Manager : public QObject {
int processedCount() const { return m_processedCount.load(std::memory_order_relaxed); } int processedCount() const { return m_processedCount.load(std::memory_order_relaxed); }
// Total count of processing items, NOT the count of items in the model
// (Why did I name this method like this? idk)
int totalCount() const { return m_watcher.progressMaximum(); } int totalCount() const { return m_watcher.progressMaximum(); }
void setSortType(Config::SortType type) { m_proxyModel->setSortType(type); } void setSortType(Config::SortType type) { m_proxyModel->setSortType(type); }
@@ -46,6 +50,8 @@ class Manager : public QObject {
QString searchText() const { return m_proxyModel->getSearchText(); } QString searchText() const { return m_proxyModel->getSearchText(); }
void loadAndProcess();
void loadAndProcess(const QStringList& paths); void loadAndProcess(const QStringList& paths);
void stop(); void stop();
@@ -59,6 +65,7 @@ class Manager : public QObject {
private: private:
void _clearData(); void _clearData();
void _process(const QStringList& paths);
signals: signals:
// Properties // Properties
@@ -75,6 +82,7 @@ class Manager : public QObject {
ProxyModel* m_proxyModel; ProxyModel* m_proxyModel;
QHash<QString, Data*> m_dataMap; QHash<QString, Data*> m_dataMap;
Config::Manager& m_configMgr;
Cache::Manager& m_cacheMgr; Cache::Manager& m_cacheMgr;
QSize m_thumbnailSize; QSize m_thumbnailSize;
+2 -1
View File
@@ -36,6 +36,7 @@ class Bootstrap {
} }
imageMgr = new Image::Manager( imageMgr = new Image::Manager(
*configMgr,
*cacheMgr, *cacheMgr,
configMgr->getFocusImageSize()); configMgr->getFocusImageSize());
@@ -55,7 +56,7 @@ class Bootstrap {
void start() { void start() {
cacheMgr->evictOldEntries(); cacheMgr->evictOldEntries();
configMgr->captureState(); configMgr->captureState();
imageMgr->loadAndProcess(configMgr->getWallpapers()); imageMgr->loadAndProcess();
} }
bool apply(const QString& path) { bool apply(const QString& path) {
+4
View File
@@ -171,6 +171,10 @@ class Carousel : public QObject {
} }
} }
Q_INVOKABLE void requestReload() {
m_imageMgr->loadAndProcess();
}
signals: signals:
void currentImageIdChanged(); void currentImageIdChanged();
void currentIndexChanged(); void currentIndexChanged();
+1
View File
@@ -28,6 +28,7 @@ qt_add_qml_module(${UILIB_NAME}_Modules
Modules/ColorControl.qml Modules/ColorControl.qml
Modules/TopBar.qml Modules/TopBar.qml
Modules/BottomBar.qml Modules/BottomBar.qml
Modules/ReloadButton.qml
) )
qt_add_qml_module(${UILIB_NAME}_Components qt_add_qml_module(${UILIB_NAME}_Components
STATIC STATIC
+17
View File
@@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls
ToolButton {
id: reloadBtn
property bool isLoading: false
icon.name: "view-refresh"
icon.width: 16
icon.height: 16
focusPolicy: Qt.NoFocus
ToolTip.visible: hovered
ToolTip.delay: 600
ToolTip.text: "Reload from disk"
enabled: !isLoading
}
+9
View File
@@ -14,10 +14,12 @@ Item {
property alias availableSortTypes: sortCtrl.availableSortTypes property alias availableSortTypes: sortCtrl.availableSortTypes
property alias selectedSortType: sortCtrl.selectedSortType property alias selectedSortType: sortCtrl.selectedSortType
property alias isSortDescending: sortCtrl.isDescending property alias isSortDescending: sortCtrl.isDescending
property alias isLoading: reloadBtn.isLoading
signal sortTypeSelected(string sortType) signal sortTypeSelected(string sortType)
signal sortDescendingToggled(bool descending) signal sortDescendingToggled(bool descending)
signal searchDismissed() signal searchDismissed()
signal reloadRequested()
function requestSearchFocus() { function requestSearchFocus() {
searchBar.requestFocus(); searchBar.requestFocus();
@@ -63,6 +65,13 @@ Item {
} }
} }
ReloadButton {
id: reloadBtn
Layout.alignment: Qt.AlignVCenter
onClicked: root.reloadRequested()
}
} }
} }
+5
View File
@@ -32,6 +32,10 @@ Item {
root.forceActiveFocus(); root.forceActiveFocus();
} }
function onReloadRequested() {
CarouselProvider.requestReload();
}
target: topBar target: topBar
} }
@@ -48,6 +52,7 @@ Item {
title: carousel.currentImageName title: carousel.currentImageName
availableSortTypes: CarouselProvider.availableSortTypes availableSortTypes: CarouselProvider.availableSortTypes
isSortDescending: CarouselProvider.sortDescending isSortDescending: CarouselProvider.sortDescending
isLoading: CarouselProvider.isLoading
onSortTypeSelected: (t) => { onSortTypeSelected: (t) => {
return CarouselProvider.setSortType(t); return CarouselProvider.setSortType(t);
} }
+1 -1
View File
@@ -143,7 +143,7 @@
}, },
"quitOnSelected": { "quitOnSelected": {
"type": "boolean", "type": "boolean",
"default": false, "default": true,
"description": "Whether to quit the application after confirming a wallpaper" "description": "Whether to quit the application after confirming a wallpaper"
}, },
"restoreOnClose": { "restoreOnClose": {
+1 -1
View File
@@ -98,7 +98,7 @@ Each item has:
`onRestore` (string, default: `""`) `onRestore` (string, default: `""`)
: Command executed on restore. Saved state keys are usable as placeholders. : Command executed on restore. Saved state keys are usable as placeholders.
`quitOnSelected` (boolean, default: `false`) `quitOnSelected` (boolean, default: `true`)
: Exit application immediately after confirming a selection. : Exit application immediately after confirming a selection.
`restoreOnClose` (boolean, default: `true`) `restoreOnClose` (boolean, default: `true`)