From f30d1d3ea165574ffbc5137a2e8ec389d6319151 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Mon, 27 Oct 2025 17:04:24 +0100 Subject: [PATCH] Add recursive wallpaper setting in WallpaperTab (implements #568) --- Assets/Translations/de.json | 4 ++ Assets/Translations/en.json | 4 ++ Assets/Translations/es.json | 4 ++ Assets/Translations/fr.json | 4 ++ Assets/Translations/pt.json | 4 ++ Assets/Translations/zh-CN.json | 4 ++ Assets/settings-default.json | 1 + Commons/Settings.qml | 1 + Modules/Settings/Tabs/WallpaperTab.qml | 8 +++ Modules/Wallpaper/WallpaperPanel.qml | 4 ++ Services/WallpaperService.qml | 93 +++++++++++++++++++++++--- 11 files changed, 123 insertions(+), 8 deletions(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 31baa596..99fb19ae 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -445,6 +445,10 @@ "description": "Pfad zu Ihrem Haupt-Hintergrundbild-Ordner.", "tooltip": "Nach Hintergrundbild-Ordner suchen" }, + "recursive-search": { + "label": "Unterordner durchsuchen", + "description": "Auch nach Hintergrundbildern in Unterordnern des Hintergrundbild-Verzeichnisses suchen." + }, "monitor-specific": { "label": "Monitor-spezifische Verzeichnisse", "description": "Unterschiedlichen Hintergrundbild-Ordner für jeden Monitor festlegen.", diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index e1f3756e..ef522020 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -445,6 +445,10 @@ "description": "Path to your main wallpaper folder.", "tooltip": "Browse for wallpaper folder" }, + "recursive-search": { + "label": "Search subfolders", + "description": "Also search for wallpapers in subfolders of the wallpaper directory." + }, "monitor-specific": { "label": "Monitor-specific directories", "description": "Set a different wallpaper folder for each monitor.", diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 48411cb7..38b07843 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -445,6 +445,10 @@ "description": "Ruta a tu carpeta principal de fondos de pantalla.", "tooltip": "Buscar carpeta de fondos de pantalla" }, + "recursive-search": { + "label": "Buscar en subcarpetas", + "description": "Buscar fondos de pantalla también en subcarpetas del directorio de fondos de pantalla." + }, "monitor-specific": { "label": "Directorios específicos por monitor", "description": "Establece una carpeta de fondos de pantalla diferente para cada monitor.", diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 273006fe..0d7367c1 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -445,6 +445,10 @@ "description": "Chemin vers votre dossier principal de fonds d'écran.", "tooltip": "Parcourir le dossier des fonds d'écran" }, + "recursive-search": { + "label": "Rechercher dans les sous-dossiers", + "description": "Rechercher également des fonds d'écran dans les sous-dossiers du répertoire des fonds d'écran." + }, "monitor-specific": { "label": "Dossiers spécifiques au moniteur", "description": "Définissez un dossier de fond d'écran différent pour chaque moniteur.", diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index bcc928fa..dc7cd739 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -407,6 +407,10 @@ "description": "Caminho para a sua pasta principal de papéis de parede.", "tooltip": "Procurar pasta de papéis de parede" }, + "recursive-search": { + "label": "Buscar em subpastas", + "description": "Também buscar papéis de parede em subpastas do diretório de papéis de parede." + }, "monitor-specific": { "label": "Diretórios específicos por monitor", "description": "Defina uma pasta de papel de parede diferente para cada monitor.", diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 85d16902..515e5ec5 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -445,6 +445,10 @@ "description": "您的主壁纸文件夹路径。", "tooltip": "浏览壁纸文件夹" }, + "recursive-search": { + "label": "搜索子文件夹", + "description": "同时在壁纸目录的子文件夹中搜索壁纸。" + }, "monitor-specific": { "label": "显示器特定目录", "description": "为每个显示器设置不同的壁纸文件夹。", diff --git a/Assets/settings-default.json b/Assets/settings-default.json index e4f3accf..dd89b056 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -93,6 +93,7 @@ "enabled": true, "directory": "", "enableMultiMonitorDirectories": false, + "recursiveSearch": false, "setWallpaperOnAllMonitors": true, "defaultWallpaper": "", "fillMode": "crop", diff --git a/Commons/Settings.qml b/Commons/Settings.qml index f584d652..24e4f6eb 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -224,6 +224,7 @@ Singleton { property bool enabled: true property string directory: "" property bool enableMultiMonitorDirectories: false + property bool recursiveSearch: false property bool setWallpaperOnAllMonitors: true property string defaultWallpaper: "" property string fillMode: "crop" diff --git a/Modules/Settings/Tabs/WallpaperTab.qml b/Modules/Settings/Tabs/WallpaperTab.qml index 775fa95e..435422b6 100644 --- a/Modules/Settings/Tabs/WallpaperTab.qml +++ b/Modules/Settings/Tabs/WallpaperTab.qml @@ -44,6 +44,14 @@ ColumnLayout { onButtonClicked: mainFolderPicker.open() } + // Recursive search + NToggle { + label: I18n.tr("settings.wallpaper.settings.recursive-search.label") + description: I18n.tr("settings.wallpaper.settings.recursive-search.description") + checked: Settings.data.wallpaper.recursiveSearch + onToggled: checked => Settings.data.wallpaper.recursiveSearch = checked + } + // Monitor-specific directories NToggle { label: I18n.tr("settings.wallpaper.settings.monitor-specific.label") diff --git a/Modules/Wallpaper/WallpaperPanel.qml b/Modules/Wallpaper/WallpaperPanel.qml index 3fe75f69..36ec59ae 100644 --- a/Modules/Wallpaper/WallpaperPanel.qml +++ b/Modules/Wallpaper/WallpaperPanel.qml @@ -291,6 +291,10 @@ NPanel { return } wallpapersList = WallpaperService.getWallpapersList(targetScreen.name) + Logger.i("WallpaperPanel", "Got", wallpapersList.length, "wallpapers for screen", targetScreen.name) + if (wallpapersList.length > 0) { + Logger.d("WallpaperPanel", "First 5 wallpapers:", wallpapersList.slice(0, 5)) + } // Pre-compute basenames once for better performance wallpapersWithNames = wallpapersList.map(function (p) { diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index bed6647c..1e6d94fb 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -72,6 +72,9 @@ Singleton { function onRandomIntervalSecChanged() { root.restartRandomWallpaperTimer() } + function onRecursiveSearchChanged() { + root.refreshWallpapersList() + } } // ------------------------------------------------- @@ -355,20 +358,94 @@ Singleton { // ------------------------------------------------------------------- function refreshWallpapersList() { - Logger.d("Wallpaper", "refreshWallpapersList") + Logger.d("Wallpaper", "refreshWallpapersList", "recursive:", Settings.data.wallpaper.recursiveSearch) scanningCount = 0 - // Force refresh by toggling the folder property on each FolderListModel - for (var i = 0; i < wallpaperScanners.count; i++) { - var scanner = wallpaperScanners.objectAt(i) - if (scanner) { - var currentFolder = scanner.folder - scanner.folder = "" - scanner.folder = currentFolder + if (Settings.data.wallpaper.recursiveSearch) { + // Use Process-based recursive search for all screens + for (var i = 0; i < Quickshell.screens.length; i++) { + var screenName = Quickshell.screens[i].name + var directory = getMonitorDirectory(screenName) + scanDirectoryRecursive(screenName, directory) + } + } else { + // Use FolderListModel (non-recursive) + for (var i = 0; i < wallpaperScanners.count; i++) { + var scanner = wallpaperScanners.objectAt(i) + if (scanner) { + var currentFolder = scanner.folder + scanner.folder = "" + scanner.folder = currentFolder + } } } } + // Process instances for recursive scanning (one per screen) + property var recursiveProcesses: ({}) + + // ------------------------------------------------------------------- + function scanDirectoryRecursive(screenName, directory) { + if (!directory || directory === "") { + Logger.w("Wallpaper", "Empty directory for", screenName) + wallpaperLists[screenName] = [] + wallpaperListChanged(screenName, 0) + return + } + + scanningCount++ + Logger.i("Wallpaper", "Starting recursive scan for", screenName, "in", directory) + + // Create Process component inline + var processComponent = Qt.createComponent("", root) + var processString = ` + import QtQuick + import Quickshell.Io + Process { + id: process + command: ["find", "` + directory + `", "-type", "f", "(", "-iname", "*.jpg", "-o", "-iname", "*.jpeg", "-o", "-iname", "*.png", "-o", "-iname", "*.gif", "-o", "-iname", "*.pnm", "-o", "-iname", "*.bmp", ")"] + stdout: StdioCollector {} + stderr: StdioCollector {} + } + ` + + var processObject = Qt.createQmlObject(processString, root, "RecursiveScan_" + screenName) + + // Store reference to avoid garbage collection + recursiveProcesses[screenName] = processObject + + var handler = function (exitCode) { + scanningCount-- + Logger.d("Wallpaper", "Process exited with code", exitCode, "for", screenName) + if (exitCode === 0) { + var lines = processObject.stdout.text.split('\n') + var files = [] + for (var i = 0; i < lines.length; i++) { + var line = lines[i].trim() + if (line !== '') { + files.push(line) + } + } + // Sort files for consistent ordering + files.sort() + wallpaperLists[screenName] = files + Logger.i("Wallpaper", "Recursive scan completed for", screenName, "found", files.length, "files") + wallpaperListChanged(screenName, files.length) + } else { + Logger.e("Wallpaper", "Recursive scan failed for", screenName, "exit code:", exitCode, "stderr:", processObject.stderr.text) + wallpaperLists[screenName] = [] + wallpaperListChanged(screenName, 0) + } + // Clean up + delete recursiveProcesses[screenName] + processObject.destroy() + } + + processObject.exited.connect(handler) + Logger.d("Wallpaper", "Starting process for", screenName) + processObject.running = true + } + // ------------------------------------------------------------------- // ------------------------------------------------------------------- // -------------------------------------------------------------------