17 Commits
Author SHA1 Message Date
Uyanide dd09e85b9e 🐛 fix: update version in CMakeLists.txt
Release / Build and Release (push) Successful in 43s
2026-08-22 03:00:46 +02:00
Uyanide 7ea701de5c 🚀 CD: add release.yml
Release / Build and Release (push) Failing after 26s
2026-08-22 02:55:49 +02:00
Uyanide 64b62246d7 🚀 CD: remove workflow 2026-08-21 18:04:41 +02:00
Uyanide a9f7d9c770 🔧 chore: update email in LICENSE 2026-08-21 17:43:19 +02:00
Uyanide 5d85acecf4 🐛 fix: pick QML controls style explicitly 2026-08-21 17:43:02 +02:00
Uyanide 2dfbd29ed1 💄 style: code style 2026-07-12 19:01:49 +02:00
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
Uyanide 9a6fa483a5 ⬆️ bump to v2.1.0
Release / Build ArchLinux Package (push) Successful in 1m3s
Release / Publish to Gitea Release (push) Successful in 3s
Release / Publish to AUR (push) Successful in 8s
2026-04-03 08:54:39 +02:00
Uyanide 5db3650184 🐛 fix: remove unused config 'printPreview' 2026-04-03 08:52:56 +02:00
Uyanide 0524f26f97 🐛 fix: forgot to implement 'printSelected' 😒 2026-04-03 08:48:49 +02:00
Uyanide 740411f194 🐛 fix: correct behaviour of --disable-actions
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-03-24 12:48:01 +01:00
Uyanide 1a2daec165 🚀 CD: refactor release workflow 2026-03-24 11:42:41 +01:00
Uyanide 524b53b7b2 CD: rename workflow to Release 2026-03-24 11:23:10 +01:00
Uyanide b5ea96bb8b 🚀 CD: add MAKEFLAGS to optimize local build process 2026-03-24 11:14:08 +01:00
Uyanide 470bb1620a 🚀 CD: 😡
CI/CD / Package (push) Successful in 2m26s
CI/CD / Publish (push) Successful in 10s
2026-03-24 11:10:24 +01:00
Uyanide b1372cacd7 🚀 CD: change license to 0BSD
CI/CD / Package (push) Successful in 59s
CI/CD / Publish (push) Successful in 10s
2026-03-24 10:40:22 +01:00
35 changed files with 322 additions and 283 deletions
+58 -120
View File
@@ -1,138 +1,76 @@
name: CI/CD name: Release
on: on:
push: push:
tags: tags:
- 'v*' - 'v*.*.*'
jobs: jobs:
build: release:
name: Package name: Build and Release
runs-on: ubuntu-latest runs-on: ubuntu-24.04
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract Version - name: Verify Tag Matches Project Version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Generate PKGBUILD
run: | run: |
TAR_URL="https://git.uyani.de/Uyanide/WallReel/archive/v${{ env.VERSION }}.tar.gz" TAG_VERSION="${GITHUB_REF_NAME#v}"
CMAKE_VERSION=$(sed -n 's/^project(WallReel VERSION \([0-9.]\+\).*/\1/p' CMakeLists.txt)
if [ "$TAG_VERSION" != "$CMAKE_VERSION" ]; then
echo "::error::Tag v${TAG_VERSION} does not match project(WallReel VERSION ${CMAKE_VERSION})"
exit 1
fi
echo "VERSION=${TAG_VERSION}" >> "$GITHUB_ENV"
wget -qO source.tar.gz "$TAR_URL" - name: Install Build Dependencies
SHA256=$(sha256sum source.tar.gz | awk '{print $1}') run: |
apt-get update
apt-get install -y --no-install-recommends \
cmake g++ jq qt6-base-dev qt6-declarative-dev
cat << 'EOF' > PKGBUILD - name: Configure
# Maintainer: Uyanide <me@uyani.de> run: |
pkgname=wallreel cmake -S . -B build \
pkgver=${{ env.VERSION }} -DCMAKE_BUILD_TYPE=Release \
pkgrel=1 -DCMAKE_INSTALL_PREFIX=/usr \
pkgdesc="Choose and set desktop wallpapers with customizable themes and actions"
arch=('x86_64')
url="https://git.uyani.de/Uyanide/WallReel"
license=('MIT')
depends=('qt6-base' 'qt6-declarative' 'gcc-libs' 'glibc')
makedepends=('cmake' 'ninja')
source=("${pkgname}-${pkgver}.tar.gz::https://git.uyani.de/Uyanide/WallReel/archive/v${pkgver}.tar.gz")
sha256sums=('INSERT_SHA256_HERE')
build() {
cd "wallreel"
cmake -B build -S . -G Ninja \
-DCMAKE_BUILD_TYPE='Release' \
-DCMAKE_INSTALL_PREFIX='/usr' \
-Wno-dev -Wno-dev
cmake --build build
- name: Build
run: cmake --build build --parallel "$(nproc)"
- name: Publish Release
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
AUTH="Authorization: token ${TOKEN}"
NOTES=$(git tag -l --format='%(contents)' "$GITHUB_REF_NAME")
payload() {
jq -n --arg tag "$GITHUB_REF_NAME" \
--arg name "WallReel ${VERSION}" \
--arg body "$NOTES" \
"$1"
} }
package() { code=$(curl -sS -o /tmp/resp.json -w '%{http_code}' -X POST "$API/releases" \
cd "wallreel" -H "$AUTH" -H 'Content-Type: application/json' \
DESTDIR="$pkgdir" cmake --install build --data-binary "$(payload '{tag_name:$tag, name:$name, body:$body, draft:false, prerelease:false}')")
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}
EOF
sed -i "s/INSERT_SHA256_HERE/$SHA256/" PKGBUILD if [ "$code" = "409" ]; then
echo "Release for ${GITHUB_REF_NAME} already exists, updating in place."
id=$(curl -sS -H "$AUTH" "$API/releases/tags/${GITHUB_REF_NAME}" | jq -r '.id')
code=$(curl -sS -o /tmp/resp.json -w '%{http_code}' -X PATCH "$API/releases/${id}" \
-H "$AUTH" -H 'Content-Type: application/json' \
--data-binary "$(payload '{name:$name, body:$body, draft:false, prerelease:false}')")
fi
- name: Build and Generate AUR Meta case "$code" in
run: | 2*) echo "Release published (HTTP ${code})." ;;
tar -cf - . | docker run --rm -i archlinux:latest /bin/bash -e -c " *) echo "::error::Gitea API returned HTTP ${code}"; cat /tmp/resp.json; exit 1 ;;
mkdir -p /workspace && cd /workspace esac
tar -xf -
exec 3>&1 1>&2
pacman-key --init && pacman-key --populate
pacman -Sy --noconfirm archlinux-keyring
pacman -Su --noconfirm base-devel cmake ninja qt6-base qt6-declarative sudo
useradd -m builduser
chown -R builduser:builduser /workspace
echo 'builduser ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
su - builduser -c 'cd /workspace && makepkg -sf --noconfirm'
su - builduser -c 'cd /workspace && makepkg --printsrcinfo' > SRCINFO.txt
tar -cf - *.pkg.tar.zst PKGBUILD LICENSE SRCINFO.txt >&3
" | tar -xf -
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: release-artifacts
path: |
*.pkg.tar.zst
PKGBUILD
LICENSE
SRCINFO.txt
release:
name: Publish
needs: build
runs-on: ubuntu-latest
steps:
- name: Extract Version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Download Artifacts
uses: actions/download-artifact@v3
with:
name: release-artifacts
path: .
- name: Publish to Gitea Release
uses: softprops/action-gh-release@v1
with:
name: WallReel ${{ env.VERSION }}
draft: false
prerelease: false
files: "*.pkg.tar.zst"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to AUR
env:
AUR_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "$AUR_KEY" > ~/.ssh/aur
chmod 600 ~/.ssh/aur
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
cat <<EOF > ~/.ssh/config
Host aur.archlinux.org
IdentityFile ~/.ssh/aur
User aur
EOF
git config --global user.name "Uyanide"
git config --global user.email "me@uyani.de"
git clone ssh://aur@aur.archlinux.org/wallreel.git aur-repo
cp PKGBUILD LICENSE aur-repo/
cp SRCINFO.txt aur-repo/.SRCINFO
cd aur-repo
git add PKGBUILD LICENSE .SRCINFO
git commit -m "Release v${{ env.VERSION }}"
git push origin master
+1 -1
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(WallReel VERSION 2.0.0 LANGUAGES CXX) project(WallReel VERSION 2.2.2 LANGUAGES CXX)
set(EXECUTABLE_NAME "wallreel") set(EXECUTABLE_NAME "wallreel")
set(CORELIB_NAME "wallreel-core") set(CORELIB_NAME "wallreel-core")
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2026 Uyanide me@uyani.de Copyright 2026 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: 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:
+1 -2
View File
@@ -117,12 +117,11 @@ Configures system commands to execute on specific events mapping to your window
| :-------------------- | :--------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | :-------------------- | :--------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `previewDebounceTime` | Integer | `300` | Debounce time (ms) for triggering the preview action. | | `previewDebounceTime` | Integer | `300` | Debounce time (ms) for triggering the preview action. |
| `printSelected` | Boolean | `true` | Print selected wallpaper path to stdout on confirm. | | `printSelected` | Boolean | `true` | Print selected wallpaper path to stdout on confirm. |
| `printPreview` | Boolean | `false` | Print previewed wallpaper path to stdout on preview. |
| `onSelected` | String | `""` | Command to execute when a wallpaper is confirmed. | | `onSelected` | String | `""` | Command to execute when a wallpaper is confirmed. |
| `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:
+10 -10
View File
@@ -1,6 +1,6 @@
.\" Automatically generated by Pandoc 3.5 .\" Automatically generated by Pandoc 3.9.0.2
.\" .\"
.TH "WALLREEL" "1" "2026\-03\-24" "WallReel 2.0.0" "User Commands" .TH "WALLREEL" "1" "2026\-03\-24" "WallReel 2.0.2" "User Commands"
.SH NAME .SH NAME
wallreel \- Choose and set desktop wallpapers with customizable themes wallreel \- Choose and set desktop wallpapers with customizable themes
and actions and actions
@@ -42,20 +42,20 @@ In this mode, the configuration is still parsed.
Action placeholders are resolved from the selected image and any Action placeholders are resolved from the selected image and any
captured state values. captured state values.
.SH BEHAVIOR NOTES .SH BEHAVIOR NOTES
.IP \[bu] 2 .IP \(bu 2
CLI options are generally optional; configuration is the preferred CLI options are generally optional; configuration is the preferred
customization path. customization path.
.IP \[bu] 2 .IP \(bu 2
Some options are mutually exclusive (for example \f[CR]\-\-verbose\f[R] Some options are mutually exclusive (for example \f[CR]\-\-verbose\f[R]
and \f[CR]\-\-quiet\f[R]). and \f[CR]\-\-quiet\f[R]).
.IP \[bu] 2 .IP \(bu 2
With \f[CR]\-\-apply\f[R], WallReel executes configured selection With \f[CR]\-\-apply\f[R], WallReel executes configured selection
actions without opening the UI. actions without opening the UI.
.SH FILES .SH FILES
\f[CR]\[ti]/.config/wallreel/config.json\f[R] : Default configuration \f[CR]\(ti/.config/wallreel/config.json\f[R] : Default configuration
file location. file location.
.PP .PP
\f[CR]\[ti]/.cache/wallreel/\f[R] : Runtime cache location. \f[CR]\(ti/.cache/wallreel/\f[R] : Runtime cache location.
.SH EXAMPLES .SH EXAMPLES
Run with default configuration: Run with default configuration:
.IP .IP
@@ -66,19 +66,19 @@ wallreel
Use a custom configuration file: Use a custom configuration file:
.IP .IP
.EX .EX
wallreel \-\-config\-file \[ti]/.config/wallreel/config.json wallreel \-\-config\-file \(ti/.config/wallreel/config.json
.EE .EE
.PP .PP
Append additional search directories: Append additional search directories:
.IP .IP
.EX .EX
wallreel \-\-append\-dir \[ti]/Pictures/Wallpapers \-\-append\-dir \[ti]/Art wallreel \-\-append\-dir \(ti/Pictures/Wallpapers \-\-append\-dir \(ti/Art
.EE .EE
.PP .PP
Apply a wallpaper and exit: Apply a wallpaper and exit:
.IP .IP
.EX .EX
wallreel \-\-apply \[ti]/Pictures/wallpaper.jpg wallreel \-\-apply \(ti/Pictures/wallpaper.jpg
.EE .EE
.SH EXIT STATUS .SH EXIT STATUS
Returns \f[CR]0\f[R] on success. Returns \f[CR]0\f[R] on success.
+61 -64
View File
@@ -1,22 +1,22 @@
.\" Automatically generated by Pandoc 3.5 .\" Automatically generated by Pandoc 3.9.0.2
.\" .\"
.TH "WALLREEL" "5" "2026\-03\-24" "WallReel 2.0.0" "File Formats Manual" .TH "WALLREEL" "5" "2026\-03\-24" "WallReel 2.0.2" "File Formats Manual"
.SH NAME .SH NAME
wallreel\-config \- configuration format for wallreel wallreel\-config \- configuration format for wallreel
.SH SYNOPSIS .SH SYNOPSIS
\f[CR]\[ti]/.config/wallreel/config.json\f[R] \f[CR]\(ti/.config/wallreel/config.json\f[R]
.SH DESCRIPTION .SH DESCRIPTION
WallReel reads configuration from a JSON document. WallReel reads configuration from a JSON document.
The root object is divided into five sections: The root object is divided into five sections:
.IP \[bu] 2 .IP \(bu 2
\f[CR]wallpaper\f[R] \f[CR]wallpaper\f[R]
.IP \[bu] 2 .IP \(bu 2
\f[CR]theme\f[R] \f[CR]theme\f[R]
.IP \[bu] 2 .IP \(bu 2
\f[CR]action\f[R] \f[CR]action\f[R]
.IP \[bu] 2 .IP \(bu 2
\f[CR]style\f[R] \f[CR]style\f[R]
.IP \[bu] 2 .IP \(bu 2
\f[CR]cache\f[R] \f[CR]cache\f[R]
.PP .PP
For complete machine\-readable validation details, refer to For complete machine\-readable validation details, refer to
@@ -25,7 +25,7 @@ For complete machine\-readable validation details, refer to
Defines where WallReel looks for images and what to exclude. Defines where WallReel looks for images and what to exclude.
.PP .PP
If both \f[CR]paths\f[R] and \f[CR]dirs\f[R] are empty or omitted, If both \f[CR]paths\f[R] and \f[CR]dirs\f[R] are empty or omitted,
WallReel defaults to recursively scanning the user\[aq]s Pictures WallReel defaults to recursively scanning the user\(aqs Pictures
directory and treating all supported image files as wallpaper directory and treating all supported image files as wallpaper
candidates. candidates.
.PP .PP
@@ -36,9 +36,9 @@ to specific image files.
to scan for images. to scan for images.
.PP .PP
Each item has: Each item has:
.IP \[bu] 2 .IP \(bu 2
\f[CR]path\f[R] (string) \f[CR]path\f[R] (string)
.IP \[bu] 2 .IP \(bu 2
\f[CR]recursive\f[R] (boolean) \f[CR]recursive\f[R] (boolean)
.PP .PP
\f[CR]excludes\f[R] (array of string, default: \f[CR][]\f[R]) : Exclude \f[CR]excludes\f[R] (array of string, default: \f[CR][]\f[R]) : Exclude
@@ -54,16 +54,16 @@ the primary color.
palette definitions. palette definitions.
.PP .PP
Each palette has: Each palette has:
.IP \[bu] 2 .IP \(bu 2
\f[CR]name\f[R] (string) \f[CR]name\f[R] (string)
.IP \[bu] 2 .IP \(bu 2
\f[CR]colors\f[R] (array) \f[CR]colors\f[R] (array)
.PP .PP
Each color item has: Each color item has:
.IP \[bu] 2 .IP \(bu 2
\f[CR]name\f[R] (string) \f[CR]name\f[R] (string)
.IP \[bu] 2 .IP \(bu 2
\f[CR]value\f[R] (hex string, for example \f[CR]\[dq]#89b4fa\[dq]\f[R]) \f[CR]value\f[R] (hex string, for example \f[CR]\(dq#89b4fa\(dq\f[R])
.SH ACTION SECTION .SH ACTION SECTION
Configures commands executed for preview, selection, and restore Configures commands executed for preview, selection, and restore
behavior. behavior.
@@ -74,33 +74,30 @@ Debounce interval in milliseconds for preview actions.
\f[CR]printSelected\f[R] (boolean, default: \f[CR]true\f[R]) : Print \f[CR]printSelected\f[R] (boolean, default: \f[CR]true\f[R]) : Print
selected wallpaper path to stdout on confirmation. selected wallpaper path to stdout on confirmation.
.PP .PP
\f[CR]printPreview\f[R] (boolean, default: \f[CR]false\f[R]) : Print \f[CR]onSelected\f[R] (string, default: \f[CR]\(dq\(dq\f[R]) : Command
previewed wallpaper path to stdout on preview.
.PP
\f[CR]onSelected\f[R] (string, default: \f[CR]\[dq]\[dq]\f[R]) : Command
executed when a wallpaper is confirmed. executed when a wallpaper is confirmed.
.PP .PP
\f[CR]onPreview\f[R] (string, default: \f[CR]\[dq]\[dq]\f[R]) : Command \f[CR]onPreview\f[R] (string, default: \f[CR]\(dq\(dq\f[R]) : Command
executed when a wallpaper is previewed. executed when a wallpaper is previewed.
.PP .PP
\f[CR]saveState\f[R] (array of object, default: \f[CR][]\f[R]) : \f[CR]saveState\f[R] (array of object, default: \f[CR][]\f[R]) :
Commands for capturing system values before changing wallpaper. Commands for capturing system values before changing wallpaper.
.PP .PP
Each item has: Each item has:
.IP \[bu] 2 .IP \(bu 2
\f[CR]key\f[R] (placeholder key) \f[CR]key\f[R] (placeholder key)
.IP \[bu] 2 .IP \(bu 2
\f[CR]fallback\f[R] (default value) \f[CR]fallback\f[R] (default value)
.IP \[bu] 2 .IP \(bu 2
\f[CR]command\f[R] (stdout\-mapped command) \f[CR]command\f[R] (stdout\-mapped command)
.IP \[bu] 2 .IP \(bu 2
\f[CR]timeout\f[R] (milliseconds) \f[CR]timeout\f[R] (milliseconds)
.PP .PP
\f[CR]onRestore\f[R] (string, default: \f[CR]\[dq]\[dq]\f[R]) : Command \f[CR]onRestore\f[R] (string, default: \f[CR]\(dq\(dq\f[R]) : Command
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
@@ -117,13 +114,13 @@ The following placeholders are available in \f[CR]onSelected\f[R],
wallpaper. wallpaper.
.PP .PP
\f[CR]{{ palette }}\f[R] : Selected palette name \f[CR]{{ palette }}\f[R] : Selected palette name
(\f[CR]\[dq]null\[dq]\f[R] if none). (\f[CR]\(dqnull\(dq\f[R] if none).
.PP .PP
\f[CR]{{ colorName }}\f[R] : Chosen primary color name \f[CR]{{ colorName }}\f[R] : Chosen primary color name
(\f[CR]\[dq]null\[dq]\f[R] if none). (\f[CR]\(dqnull\(dq\f[R] if none).
.PP .PP
\f[CR]{{ colorHex }}\f[R] : Chosen primary color hex \f[CR]{{ colorHex }}\f[R] : Chosen primary color hex
(\f[CR]\[dq]null\[dq]\f[R] if none). (\f[CR]\(dqnull\(dq\f[R] if none).
.PP .PP
\f[CR]{{ domColorHex }}\f[R] : Dominant color hex extracted from the \f[CR]{{ domColorHex }}\f[R] : Dominant color hex extracted from the
wallpaper. wallpaper.
@@ -162,54 +159,54 @@ Older entries are evicted.
.IP .IP
.EX .EX
{ {
\[dq]$schema\[dq]: \[dq]https://raw.githubusercontent.com/Uyanide/WallReel/refs/heads/master/config.schema.json\[dq], \(dq$schema\(dq: \(dqhttps://raw.githubusercontent.com/Uyanide/WallReel/refs/heads/master/config.schema.json\(dq,
\[dq]wallpaper\[dq]: { \(dqwallpaper\(dq: {
\[dq]paths\[dq]: [\[dq]/home/user/Pictures/favorite.jpg\[dq]], \(dqpaths\(dq: [\(dq/home/user/Pictures/favorite.jpg\(dq],
\[dq]dirs\[dq]: [ \(dqdirs\(dq: [
{ {
\[dq]path\[dq]: \[dq]/home/user/Pictures/Wallpapers\[dq], \(dqpath\(dq: \(dq/home/user/Pictures/Wallpapers\(dq,
\[dq]recursive\[dq]: \f[B]true\f[R] \(dqrecursive\(dq: \f[B]true\f[R]
} }
], ],
\[dq]excludes\[dq]: [\[dq]\[rs]\[rs].gif$\[dq]] \(dqexcludes\(dq: [\(dq\(rs\(rs.gif$\(dq]
}, },
\[dq]theme\[dq]: { \(dqtheme\(dq: {
\[dq]palettes\[dq]: [ \(dqpalettes\(dq: [
{ {
\[dq]name\[dq]: \[dq]Dark\[dq], \(dqname\(dq: \(dqDark\(dq,
\[dq]colors\[dq]: [ \(dqcolors\(dq: [
{ \[dq]name\[dq]: \[dq]blue\[dq], \[dq]value\[dq]: \[dq]#89b4fa\[dq] }, { \(dqname\(dq: \(dqblue\(dq, \(dqvalue\(dq: \(dq#89b4fa\(dq },
{ \[dq]name\[dq]: \[dq]red\[dq], \[dq]value\[dq]: \[dq]#f38ba8\[dq] } { \(dqname\(dq: \(dqred\(dq, \(dqvalue\(dq: \(dq#f38ba8\(dq }
] ]
} }
] ]
}, },
\[dq]action\[dq]: { \(dqaction\(dq: {
\[dq]previewDebounceTime\[dq]: 500, \(dqpreviewDebounceTime\(dq: 500,
\[dq]quitOnSelected\[dq]: \f[B]true\f[R], \(dqquitOnSelected\(dq: \f[B]true\f[R],
\[dq]onPreview\[dq]: \[dq]swww img {{ path }}\[dq], \(dqonPreview\(dq: \(dqswww img {{ path }}\(dq,
\[dq]onSelected\[dq]: \[dq]cp {{ path }} \[ti]/.config/wallpaper/current/ && swww img {{ path }}\[dq], \(dqonSelected\(dq: \(dqcp {{ path }} \(ti/.config/wallpaper/current/ && swww img {{ path }}\(dq,
\[dq]saveState\[dq]: [ \(dqsaveState\(dq: [
{ {
\[dq]key\[dq]: \[dq]current_wp\[dq], \(dqkey\(dq: \(dqcurrent_wp\(dq,
\[dq]fallback\[dq]: \[dq]/home/user/Pictures/default.jpg\[dq], \(dqfallback\(dq: \(dq/home/user/Pictures/default.jpg\(dq,
\[dq]command\[dq]: \[dq]find \[ti]/.config/wallpaper/current \-type f | head \-n 1\[dq], \(dqcommand\(dq: \(dqfind \(ti/.config/wallpaper/current \-type f | head \-n 1\(dq,
\[dq]timeout\[dq]: 1000 \(dqtimeout\(dq: 1000
} }
], ],
\[dq]onRestore\[dq]: \[dq]swww img {{ current_wp }}\[dq] \(dqonRestore\(dq: \(dqswww img {{ current_wp }}\(dq
}, },
\[dq]style\[dq]: { \(dqstyle\(dq: {
\[dq]image_width\[dq]: 640, \(dqimage_width\(dq: 640,
\[dq]image_height\[dq]: 400, \(dqimage_height\(dq: 400,
\[dq]image_focus_scale\[dq]: 1.2, \(dqimage_focus_scale\(dq: 1.2,
\[dq]window_width\[dq]: 1280, \(dqwindow_width\(dq: 1280,
\[dq]window_height\[dq]: 720 \(dqwindow_height\(dq: 720
}, },
\[dq]cache\[dq]: { \(dqcache\(dq: {
\[dq]saveSortMethod\[dq]: \f[B]true\f[R], \(dqsaveSortMethod\(dq: \f[B]true\f[R],
\[dq]savePalette\[dq]: \f[B]true\f[R], \(dqsavePalette\(dq: \f[B]true\f[R],
\[dq]maxImageEntries\[dq]: 300 \(dqmaxImageEntries\(dq: 300
} }
} }
.EE .EE
+2 -6
View File
@@ -1,8 +1,6 @@
#ifndef WALLREEL_CONFIG_DATA_HPP #ifndef WALLREEL_CONFIG_DATA_HPP
#define WALLREEL_CONFIG_DATA_HPP #define WALLREEL_CONFIG_DATA_HPP
#include <qtmetamacros.h>
#include <QColor> #include <QColor>
#include <QHash> #include <QHash>
#include <QList> #include <QList>
@@ -27,7 +25,6 @@
// //
// action.previewDebounceTime number 300 Debounce time for preview action in milliseconds // action.previewDebounceTime number 300 Debounce time for preview action in milliseconds
// action.printSelected boolean true Whether to print the selected wallpaper path to stdout on confirm // action.printSelected boolean true Whether to print the selected wallpaper path to stdout on confirm
// action.printPreview boolean false Whether to print the previewed wallpaper path to stdout on preview
// action.onSelected string "" Command to execute on confirmation // action.onSelected string "" Command to execute on confirmation
// action.onPreview string "" Command to execute on preview // action.onPreview string "" Command to execute on preview
// action.saveState array [] Useful for restore command // action.saveState array [] Useful for restore command
@@ -36,7 +33,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
@@ -126,8 +123,7 @@ struct ActionConfigItems {
QString onRestore; QString onRestore;
int previewDebounceTime = 300; // milliseconds int previewDebounceTime = 300; // milliseconds
bool printSelected = true; bool printSelected = true;
bool printPreview = false; bool quitOnSelected = true;
bool quitOnSelected = false;
bool restoreOnClose = true; bool restoreOnClose = true;
}; };
+10 -11
View File
@@ -26,8 +26,9 @@ Manager::Manager(
const QDir& picturesDir, const QDir& picturesDir,
const QStringList& searchDirs, const QStringList& searchDirs,
const QString& configPath, const QString& configPath,
bool disableActions,
QObject* parent) QObject* parent)
: QObject(parent), m_configDir(configDir) { : QObject(parent), m_configDir(configDir), m_disableActions(disableActions) {
connect(this, &Manager::stateCaptured, this, [this]() { connect(this, &Manager::stateCaptured, this, [this]() {
m_stateCaptured = true; m_stateCaptured = true;
WR_INFO("State capture completed"); WR_INFO("State capture completed");
@@ -53,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() {
@@ -202,12 +200,6 @@ void Manager::_loadActionConfig(const QJsonObject& root) {
m_actionConfig.printSelected = val.toBool(); m_actionConfig.printSelected = val.toBool();
} }
} }
if (config.contains("printPreview")) {
const auto& val = config["printPreview"];
if (val.isBool()) {
m_actionConfig.printPreview = val.toBool();
}
}
if (config.contains("saveState") && config["saveState"].isArray()) { if (config.contains("saveState") && config["saveState"].isArray()) {
const QJsonArray& arr = config["saveState"].toArray(); const QJsonArray& arr = config["saveState"].toArray();
for (const auto& item : arr) { for (const auto& item : arr) {
@@ -329,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
@@ -398,6 +390,13 @@ void Manager::captureState() {
if (m_stateCaptured) { if (m_stateCaptured) {
WR_DEBUG("State already captured, skipping capture"); WR_DEBUG("State already captured, skipping capture");
emit stateCaptured(); emit stateCaptured();
return;
}
if (m_disableActions) {
WR_DEBUG("Actions are disabled, skipping state capture");
emit stateCaptured();
return;
} }
if (m_pendingCaptures > 0) { if (m_pendingCaptures > 0) {
+9 -5
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_CONFIGMGR_HPP #ifndef WALLREEL_CONFIG_MANAGER_HPP
#define WALLREEL_CONFIGMGR_HPP #define WALLREEL_CONFIG_MANAGER_HPP
#include <QDir> #include <QDir>
@@ -27,6 +27,7 @@ class Manager : public QObject {
* @param searchDirs Additional directories to search for wallpapers (not recursive) * @param searchDirs Additional directories to search for wallpapers (not recursive)
* @param configPath Optional path to a specific configuration file (overrides the default config path) * @param configPath Optional path to a specific configuration file (overrides the default config path)
* @param picturesDir The pictures directory (default location for user wallpapers) * @param picturesDir The pictures directory (default location for user wallpapers)
* @param disableActions Whether to disable actions
* @param parent QObject parent * @param parent QObject parent
* *
* @note The constructor will load the configuration and scan for wallpapers immediately. * @note The constructor will load the configuration and scan for wallpapers immediately.
@@ -36,6 +37,7 @@ class Manager : public QObject {
const QDir& picturesDir, const QDir& picturesDir,
const QStringList& searchDirs = {}, const QStringList& searchDirs = {},
const QString& configPath = "", const QString& configPath = "",
bool disableActions = false,
QObject* parent = nullptr); QObject* parent = nullptr);
~Manager(); ~Manager();
@@ -70,6 +72,8 @@ class Manager : public QObject {
*/ */
Q_INVOKABLE void captureState(); Q_INVOKABLE void captureState();
void scanWallpapers();
signals: signals:
void stateCaptured(); void stateCaptured();
@@ -81,13 +85,13 @@ 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);
private: private:
const QDir m_configDir; const QDir m_configDir;
bool m_disableActions = false;
WallpaperConfigItems m_wallpaperConfig; WallpaperConfigItems m_wallpaperConfig;
ThemeConfigItems m_themeConfig; ThemeConfigItems m_themeConfig;
ActionConfigItems m_actionConfig; ActionConfigItems m_actionConfig;
@@ -102,4 +106,4 @@ class Manager : public QObject {
} // namespace WallReel::Core::Config } // namespace WallReel::Core::Config
#endif // WALLREEL_CONFIGMGR_HPP #endif // WALLREEL_CONFIG_MANAGER_HPP
+3 -3
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_IMAGEDATA_HPP #ifndef WALLREEL_IMAGE_DATA_HPP
#define WALLREEL_IMAGEDATA_HPP #define WALLREEL_IMAGE_DATA_HPP
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@@ -106,4 +106,4 @@ class Data {
} // namespace WallReel::Core::Image } // namespace WallReel::Core::Image
#endif // WALLREEL_IMAGEDATA_HPP #endif // WALLREEL_IMAGE_DATA_HPP
+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) {
+11 -3
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_IMAGEMANAGER_HPP #ifndef WALLREEL_IMAGE_MANAGER_HPP
#define WALLREEL_IMAGEMANAGER_HPP #define WALLREEL_IMAGE_MANAGER_HPP
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDir> #include <QDir>
@@ -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;
@@ -88,4 +96,4 @@ class Manager : public QObject {
} // namespace WallReel::Core::Image } // namespace WallReel::Core::Image
#endif // WALLREEL_IMAGEMANAGER_HPP #endif // WALLREEL_IMAGE_MANAGER_HPP
+5 -5
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_IMAGEMODEL_HPP #ifndef WALLREEL_IMAGE_MODEL_HPP
#define WALLREEL_IMAGEMODEL_HPP #define WALLREEL_IMAGE_MODEL_HPP
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
@@ -78,10 +78,10 @@ class ProxyModel : public QSortFilterProxyModel {
private: private:
QString m_searchText; QString m_searchText;
Config::SortType m_sortType; Config::SortType m_sortType = Config::SortType::Date;
bool m_sortDescending; bool m_sortDescending = true;
}; };
} // namespace WallReel::Core::Image } // namespace WallReel::Core::Image
#endif // WALLREEL_IMAGEMODEL_HPP #endif // WALLREEL_IMAGE_MODEL_HPP
-2
View File
@@ -1,8 +1,6 @@
#ifndef WALLREEL_PALETTE_DATA_HPP #ifndef WALLREEL_PALETTE_DATA_HPP
#define WALLREEL_PALETTE_DATA_HPP #define WALLREEL_PALETTE_DATA_HPP
#include <qcolor.h>
#include <QColor> #include <QColor>
#include <QList> #include <QList>
#include <QObject> #include <QObject>
+3 -3
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_CORE_PALETTE_DOMCOLOR_HPP #ifndef WALLREEL_PALETTE_DOMCOLOR_HPP
#define WALLREEL_CORE_PALETTE_DOMCOLOR_HPP #define WALLREEL_PALETTE_DOMCOLOR_HPP
#include <QImage> #include <QImage>
@@ -15,4 +15,4 @@ QColor getDominantColor(const QImage& image);
} // namespace WallReel::Core::Palette } // namespace WallReel::Core::Palette
#endif // WALLREEL_CORE_PALETTE_DOMCOLOR_HPP #endif // WALLREEL_PALETTE_DOMCOLOR_HPP
-2
View File
@@ -1,7 +1,5 @@
#include "manager.hpp" #include "manager.hpp"
#include <qcontainerfwd.h>
#include "Palette/matchcolor.hpp" #include "Palette/matchcolor.hpp"
#include "Utils/misc.hpp" #include "Utils/misc.hpp"
#include "logger.hpp" #include "logger.hpp"
-2
View File
@@ -1,8 +1,6 @@
#ifndef WALLREEL_PALETTE_MANAGER_HPP #ifndef WALLREEL_PALETTE_MANAGER_HPP
#define WALLREEL_PALETTE_MANAGER_HPP #define WALLREEL_PALETTE_MANAGER_HPP
#include <qcolor.h>
#include "Config/data.hpp" #include "Config/data.hpp"
#include "Image/manager.hpp" #include "Image/manager.hpp"
#include "data.hpp" #include "data.hpp"
+3 -3
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_CORE_PALETTE_MATCHCOLOR_HPP #ifndef WALLREEL_PALETTE_MATCHCOLOR_HPP
#define WALLREEL_CORE_PALETTE_MATCHCOLOR_HPP #define WALLREEL_PALETTE_MATCHCOLOR_HPP
#include "data.hpp" #include "data.hpp"
@@ -16,4 +16,4 @@ ColorItem bestMatch(const QColor& target, const QList<ColorItem>& candidates);
} // namespace WallReel::Core::Palette } // namespace WallReel::Core::Palette
#endif // WALLREEL_CORE_PALETTE_MATCHCOLOR_HPP #endif // WALLREEL_PALETTE_MATCHCOLOR_HPP
+11 -3
View File
@@ -18,12 +18,13 @@ class Bootstrap {
friend class Carousel; friend class Carousel;
public: public:
Bootstrap(const AppOptions& options) { Bootstrap(const AppOptions& options) : options(options) {
configMgr = new Config::Manager( configMgr = new Config::Manager(
Utils::getConfigDir(), Utils::getConfigDir(),
Utils::getPicturesDir(), Utils::getPicturesDir(),
options.appendDirs, options.appendDirs,
options.configPath); options.configPath,
options.disableActions);
cacheMgr = new Cache::Manager( cacheMgr = new Cache::Manager(
Utils::getCacheDir(), Utils::getCacheDir(),
@@ -35,6 +36,7 @@ class Bootstrap {
} }
imageMgr = new Image::Manager( imageMgr = new Image::Manager(
*configMgr,
*cacheMgr, *cacheMgr,
configMgr->getFocusImageSize()); configMgr->getFocusImageSize());
@@ -54,10 +56,15 @@ 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) {
if (options.disableActions) {
Logger::warn("Bootstrap", "Actions are disabled, cannot apply wallpaper");
return false;
}
QEventLoop loop; QEventLoop loop;
bool successFlag = false; bool successFlag = false;
@@ -122,6 +129,7 @@ class Bootstrap {
} }
private: private:
const AppOptions& options;
Cache::Manager* cacheMgr{}; Cache::Manager* cacheMgr{};
Config::Manager* configMgr{}; Config::Manager* configMgr{};
Image::Manager* imageMgr{}; Image::Manager* imageMgr{};
+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();
+3
View File
@@ -1,5 +1,6 @@
#include "manager.hpp" #include "manager.hpp"
#include "Utils/misc.hpp"
#include "Utils/texttemplate.hpp" #include "Utils/texttemplate.hpp"
#include "logger.hpp" #include "logger.hpp"
@@ -61,6 +62,8 @@ void Manager::selectWallpaper(const QString& id) {
return; return;
} }
Utils::printPath(data->getFullPath());
const auto command = _renderCommand(m_actionConfig.onSelected, _generateVariables(*data)); const auto command = _renderCommand(m_actionConfig.onSelected, _generateVariables(*data));
m_wallpaperService->select(command); m_wallpaperService->select(command);
} }
-4
View File
@@ -1,9 +1,5 @@
#include "Service/wallpaper.hpp" #include "Service/wallpaper.hpp"
#include <qprocess.h>
#include <QColor>
#include "logger.hpp" #include "logger.hpp"
WALLREEL_DECLARE_SENDER("WallpaperService") WALLREEL_DECLARE_SENDER("WallpaperService")
+3 -3
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_WALLPAPERSERVICE_HPP #ifndef WALLREEL_SERVICE_WALLPAPER_HPP
#define WALLREEL_WALLPAPERSERVICE_HPP #define WALLREEL_SERVICE_WALLPAPER_HPP
#include <QProcess> #include <QProcess>
#include <QTimer> #include <QTimer>
@@ -38,4 +38,4 @@ class WallpaperService : public QObject {
} // namespace WallReel::Core::Service } // namespace WallReel::Core::Service
#endif // WALLREEL_WALLPAPERSERVICE_HPP #endif // WALLREEL_SERVICE_WALLPAPER_HPP
+21 -3
View File
@@ -1,5 +1,5 @@
#ifndef WALLREEL_MISC_HPP #ifndef WALLREEL_UTILS_MISC_HPP
#define WALLREEL_MISC_HPP #define WALLREEL_UTILS_MISC_HPP
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@@ -179,6 +179,24 @@ inline QDir getPicturesDir() {
return QDir(picturesDir); return QDir(picturesDir);
} }
inline void printPath(const QString& path, std::FILE* out = stdout) {
if (path.isEmpty()) {
return;
}
const QByteArray bytes = QFile::encodeName(path);
const size_t n = static_cast<size_t>(bytes.size());
if (std::fwrite(bytes.constData(), 1, n, out) != n) {
return;
}
if (std::fputc('\n', out) == EOF) {
return;
}
std::fflush(out);
return;
}
} // namespace WallReel::Core::Utils } // namespace WallReel::Core::Utils
#endif // WALLREEL_MISC_HPP #endif // WALLREEL_UTILS_MISC_HPP
+3 -3
View File
@@ -1,5 +1,5 @@
#ifndef TEXTTEMPLATE_HPP #ifndef WALLREEL_UTILS_TEXTTEMPLATE_HPP
#define TEXTTEMPLATE_HPP #define WALLREEL_UTILS_TEXTTEMPLATE_HPP
#include <QMap> #include <QMap>
#include <QRegularExpression> #include <QRegularExpression>
@@ -139,4 +139,4 @@ inline QStringList extractTemplateKeys(const QString& templateStr) {
} // namespace WallReel::Core::Utils } // namespace WallReel::Core::Utils
#endif // TEXTTEMPLATE_HPP #endif // WALLREEL_UTILS_TEXTTEMPLATE_HPP
+4 -1
View File
@@ -1,4 +1,5 @@
#pragma once #ifndef WALLREEL_CORE_APPOPTIONS_HPP
#define WALLREEL_CORE_APPOPTIONS_HPP
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QStringList> #include <QStringList>
@@ -36,3 +37,5 @@ class AppOptions {
}; };
} // namespace WallReel::Core } // namespace WallReel::Core
#endif // WALLREEL_CORE_APPOPTIONS_HPP
-2
View File
@@ -1,8 +1,6 @@
#ifndef WALLREEL_LOGGER_HPP #ifndef WALLREEL_LOGGER_HPP
#define WALLREEL_LOGGER_HPP #define WALLREEL_LOGGER_HPP
#include <qcontainerfwd.h>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QString> #include <QString>
#include <cstdio> #include <cstdio>
+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);
} }
+31 -3
View File
@@ -1,8 +1,8 @@
#include <qobject.h>
#include <QApplication> #include <QApplication>
#include <QFileInfo>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QQuickStyle>
#include <QSocketNotifier> #include <QSocketNotifier>
extern "C" { extern "C" {
@@ -21,6 +21,26 @@ using namespace WallReel::Core;
WALLREEL_DECLARE_SENDER("Main") WALLREEL_DECLARE_SENDER("Main")
static QString pickControlsStyle(const QQmlApplicationEngine& engine) {
// Respect env
if (!qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE"))
return {};
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
using namespace Qt::StringLiterals;
// Prefer KDE style if available,
for (const QString& p : engine.importPathList())
if (QFileInfo::exists(p + u"/org/kde/desktop/qmldir"_s))
return u"org.kde.desktop"_s;
// fallback to Fusion
return u"Fusion"_s;
#else
for (const QString& p : engine.importPathList())
if (QFileInfo::exists(p + u"/org/kde/desktop/qmldir"_qs))
return u"org.kde.desktop"_qs;
return u"Fusion"_qs;
#endif
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// Destruction order after QApplication quits its event loop: // Destruction order after QApplication quits its event loop:
// 1. QQmlApplicationEngine (with all QML objects) // 1. QQmlApplicationEngine (with all QML objects)
@@ -46,12 +66,14 @@ int main(int argc, char* argv[]) {
QApplication a(argc, argv); QApplication a(argc, argv);
a.setApplicationName(APP_NAME); a.setApplicationName(APP_NAME);
a.setApplicationVersion(APP_VERSION); a.setApplicationVersion(APP_VERSION);
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
a.setWindowIcon(QIcon(u":/icon.svg"_s)); a.setWindowIcon(QIcon(u":/icon.svg"_s));
#else #else
a.setWindowIcon(QIcon(u":/%1.svg"_qs.arg(APP_NAME))); a.setWindowIcon(QIcon(u":/icon.svg"_qs));
#endif #endif
}
{ {
Logger::init(); Logger::init();
@@ -105,6 +127,10 @@ int main(int argc, char* argv[]) {
{ {
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
if (const QString s = pickControlsStyle(engine); !s.isEmpty()) {
QQuickStyle::setStyle(s);
}
QObject::connect( QObject::connect(
&engine, &engine,
&QQmlApplicationEngine::objectCreationFailed, &QQmlApplicationEngine::objectCreationFailed,
@@ -112,6 +138,7 @@ int main(int argc, char* argv[]) {
[]() { QCoreApplication::exit(-1); }, []() { QCoreApplication::exit(-1); },
Qt::QueuedConnection); Qt::QueuedConnection);
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
engine.loadFromModule(UIMODULE_URI, u"Main"_s); engine.loadFromModule(UIMODULE_URI, u"Main"_s);
@@ -119,6 +146,7 @@ int main(int argc, char* argv[]) {
engine.addImportPath(u"qrc:/"_qs); engine.addImportPath(u"qrc:/"_qs);
engine.load(QUrl(u"qrc:/WallReel/UI/Main.qml"_qs)); engine.load(QUrl(u"qrc:/WallReel/UI/Main.qml"_qs));
#endif #endif
}
bootstrap.start(); bootstrap.start();
+1 -6
View File
@@ -96,11 +96,6 @@
"default": true, "default": true,
"description": "Whether to print the selected wallpaper path to stdout on confirm" "description": "Whether to print the selected wallpaper path to stdout on confirm"
}, },
"printPreview": {
"type": "boolean",
"default": false,
"description": "Whether to print the previewed wallpaper path to stdout on preview"
},
"onSelected": { "onSelected": {
"type": "string", "type": "string",
"default": "", "default": "",
@@ -148,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
@@ -2,7 +2,7 @@
title: WALLREEL title: WALLREEL
section: 1 section: 1
header: User Commands header: User Commands
footer: WallReel 2.0.0 footer: WallReel 2.0.2
date: 2026-03-24 date: 2026-03-24
--- ---
+2 -5
View File
@@ -2,7 +2,7 @@
title: WALLREEL title: WALLREEL
section: 5 section: 5
header: File Formats Manual header: File Formats Manual
footer: WallReel 2.0.0 footer: WallReel 2.0.2
date: 2026-03-24 date: 2026-03-24
--- ---
@@ -79,9 +79,6 @@ Configures commands executed for preview, selection, and restore behavior.
`printSelected` (boolean, default: `true`) `printSelected` (boolean, default: `true`)
: Print selected wallpaper path to stdout on confirmation. : Print selected wallpaper path to stdout on confirmation.
`printPreview` (boolean, default: `false`)
: Print previewed wallpaper path to stdout on preview.
`onSelected` (string, default: `""`) `onSelected` (string, default: `""`)
: Command executed when a wallpaper is confirmed. : Command executed when a wallpaper is confirmed.
@@ -101,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`)