diff --git a/quickshell/Common/SessionData.qml b/quickshell/Common/SessionData.qml index 8082a1415..39372f91d 100644 --- a/quickshell/Common/SessionData.qml +++ b/quickshell/Common/SessionData.qml @@ -153,6 +153,7 @@ Singleton { property string wallpaperCyclingMode: "interval" property int wallpaperCyclingInterval: 300 property string wallpaperCyclingTime: "06:00" + property string wallpaperCyclingFolderPath: "" property var monitorCyclingSettings: ({}) property bool nightModeEnabled: false @@ -1000,6 +1001,37 @@ Singleton { saveSettings(); } + function setMonitorCyclingFolderPath(screenName, folderPath) { + var screen = null; + var screens = Quickshell.screens; + for (var i = 0; i < screens.length; i++) { + if (screens[i].name === screenName) { + screen = screens[i]; + break; + } + } + + if (!screen) { + log.warn("Screen not found"); + return; + } + + var identifier = typeof SettingsData !== "undefined" ? SettingsData.getScreenDisplayName(screen) : screen.name; + + var newSettings = {}; + for (var key in monitorCyclingSettings) { + var isThisScreen = key === screen.name || (screen.model && key === screen.model); + if (!isThisScreen) { + newSettings[key] = monitorCyclingSettings[key]; + } + } + + newSettings[identifier] = getMonitorCyclingSettings(screenName); + newSettings[identifier].folderPath = folderPath; + monitorCyclingSettings = newSettings; + saveSettings(); + } + function setNightModeEnabled(enabled) { nightModeEnabled = enabled; saveSettings(); @@ -1545,7 +1577,8 @@ Singleton { "random": false, "mode": "interval", "interval": 300, - "time": "06:00" + "time": "06:00", + "folderPath": "" }; var value = _findMonitorValue(monitorCyclingSettings, screenName); return Object.assign({}, defaults, value !== undefined ? value : {}); diff --git a/quickshell/Common/settings/SessionSpec.js b/quickshell/Common/settings/SessionSpec.js index fb3ddf8c3..d283989bf 100644 --- a/quickshell/Common/settings/SessionSpec.js +++ b/quickshell/Common/settings/SessionSpec.js @@ -24,6 +24,7 @@ var SPEC = { wallpaperCyclingMode: { def: "interval" }, wallpaperCyclingInterval: { def: 300 }, wallpaperCyclingTime: { def: "06:00" }, + wallpaperCyclingFolderPath: { def: "" }, monitorCyclingSettings: { def: {} }, nightModeEnabled: { def: false }, diff --git a/quickshell/Modals/Settings/SettingsModal.qml b/quickshell/Modals/Settings/SettingsModal.qml index d1c38d159..577ce5d24 100644 --- a/quickshell/Modals/Settings/SettingsModal.qml +++ b/quickshell/Modals/Settings/SettingsModal.qml @@ -177,6 +177,8 @@ DankFloatingWindow { fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp", "*.jxl", "*.avif", "*.heif", "*.exr"] onFileSelected: path => { SessionData.setWallpaper(path); + SessionData.wallpaperCyclingFolderPath = ""; + SessionData.saveSettings(); close(); } onDialogClosed: () => { diff --git a/quickshell/Modules/DankDash/WallpaperTab.qml b/quickshell/Modules/DankDash/WallpaperTab.qml index 27bce122b..ced8eeba4 100644 --- a/quickshell/Modules/DankDash/WallpaperTab.qml +++ b/quickshell/Modules/DankDash/WallpaperTab.qml @@ -159,8 +159,11 @@ Item { function setCurrentWallpaper(path) { if (SessionData.perMonitorWallpaper && targetScreenName) { SessionData.setMonitorWallpaper(targetScreenName, path); + SessionData.setMonitorCyclingFolderPath(targetScreenName, ""); } else { SessionData.setWallpaper(path); + SessionData.wallpaperCyclingFolderPath = ""; + SessionData.saveSettings(); } } diff --git a/quickshell/Modules/Settings/WallpaperTab.qml b/quickshell/Modules/Settings/WallpaperTab.qml index 36cd17fdf..bccce0c23 100644 --- a/quickshell/Modules/Settings/WallpaperTab.qml +++ b/quickshell/Modules/Settings/WallpaperTab.qml @@ -146,6 +146,26 @@ Item { } } + Rectangle { + width: 32 + height: 32 + radius: 16 + color: Qt.rgba(255, 255, 255, 0.9) + + DankIcon { + anchors.centerIn: parent + name: "create_new_folder" + size: 18 + color: "black" + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: root.openFolderWallpaperBrowser() + } + } + Rectangle { width: 32 height: 32 @@ -199,10 +219,13 @@ Item { onClicked: { if (SessionData.perMonitorWallpaper) { SessionData.setMonitorWallpaper(selectedMonitorName, ""); + SessionData.setMonitorCyclingFolderPath(selectedMonitorName, ""); } else { if (Theme.currentTheme === Theme.dynamic) Theme.switchTheme("blue"); SessionData.clearWallpaper(); + SessionData.wallpaperCyclingFolderPath = ""; + SessionData.saveSettings(); } } } @@ -1308,6 +1331,12 @@ Item { mainWallpaperBrowserLoader.item.open(); } + function openFolderWallpaperBrowser() { + folderWallpaperBrowserLoader.active = true; + if (folderWallpaperBrowserLoader.item) + folderWallpaperBrowserLoader.item.open(); + } + function openLightWallpaperBrowser() { lightWallpaperBrowserLoader.active = true; if (lightWallpaperBrowserLoader.item) @@ -1334,14 +1363,36 @@ Item { onFileSelected: path => { if (SessionData.perMonitorWallpaper) { SessionData.setMonitorWallpaper(selectedMonitorName, path); + SessionData.setMonitorCyclingFolderPath(selectedMonitorName, ""); } else { SessionData.setWallpaper(path); + SessionData.wallpaperCyclingFolderPath = ""; + SessionData.saveSettings(); } close(); } } } + LazyLoader { + id: folderWallpaperBrowserLoader + active: false + + FileBrowserModal { + parentModal: root.parentModal + browserTitle: I18n.tr("Choose wallpaper folder", "wallpaper folder file browser title") + browserIcon: "folder" + browserType: "wallpaper" + folderMode: true + showHiddenFiles: true + onFileSelected: path => { + var targetMonitor = SessionData.perMonitorWallpaper ? selectedMonitorName : ""; + WallpaperCyclingService.cycleFromFolder(targetMonitor, path); + close(); + } + } + } + LazyLoader { id: lightWallpaperBrowserLoader active: false diff --git a/quickshell/Services/WallpaperCyclingService.qml b/quickshell/Services/WallpaperCyclingService.qml index 2e6bb3eeb..b63eebc2e 100644 --- a/quickshell/Services/WallpaperCyclingService.qml +++ b/quickshell/Services/WallpaperCyclingService.qml @@ -148,12 +148,26 @@ Singleton { monitorProcesses = newProcesses; return process; } - function cycle(screenName, wallpaperPath, goToPrevious) { - const currentWallpaper = wallpaperPath || SessionData.wallpaperPath; + const currentWallpaper = wallpaperPath || (screenName ? SessionData.getMonitorWallpaper(screenName) : SessionData.wallpaperPath); if (!currentWallpaper) return; - const wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/')); + let wallpaperDir; + + if (screenName) { + const monitorSettings = SessionData.getMonitorCyclingSettings(screenName); + if (monitorSettings.folderPath && currentWallpaper.startsWith(monitorSettings.folderPath + "/")) { + wallpaperDir = monitorSettings.folderPath; + } else { + wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/')); + } + } else { + if (SessionData.wallpaperCyclingFolderPath && currentWallpaper.startsWith(SessionData.wallpaperCyclingFolderPath + "/")) { + wallpaperDir = SessionData.wallpaperCyclingFolderPath; + } else { + wallpaperDir = currentWallpaper.substring(0, currentWallpaper.lastIndexOf('/')); + } + } if (screenName && monitorProcessComponent.status === Component.Ready) { var process = monitorProcessFor(screenName); @@ -180,6 +194,35 @@ Singleton { cycle(screenName, wallpaperPath, true); } + function cycleFromFolder(screenName, folderPath) { + if (!folderPath) + return; + + if (screenName) { + SessionData.setMonitorCyclingFolderPath(screenName, folderPath); + } else { + SessionData.wallpaperCyclingFolderPath = folderPath; + SessionData.saveSettings(); + } + + if (screenName && monitorProcessComponent.status === Component.Ready) { + var process = monitorProcessFor(screenName); + process.command = findCommand(folderPath); + process.targetScreenName = screenName; + process.currentWallpaper = ""; + process.goToPrevious = false; + process.running = true; + return; + } + + var globalProcess = cyclingProcess; + globalProcess.command = findCommand(folderPath); + globalProcess.targetScreenName = screenName || ""; + globalProcess.currentWallpaper = ""; + globalProcess.goToPrevious = false; + globalProcess.running = true; + } + function resetScheduleAfterManual() { if (!serverSchedulingAvailable) return; @@ -224,10 +267,14 @@ Singleton { if (!text || !text.trim()) return; const files = text.trim().split('\n').filter(file => file.length > 0); - if (files.length <= 1) + if (files.length < 1) return; const wallpaperList = files.sort(); - let currentIndex = wallpaperList.findIndex(path => path === currentPath); + const isInitialFolderSelect = currentPath === ""; + let currentIndex = -1; + if (!isInitialFolderSelect) { + currentIndex = wallpaperList.findIndex(path => path === currentPath); + } if (currentIndex === -1) currentIndex = 0; @@ -239,7 +286,9 @@ Singleton { } let targetIndex; - if (isRandom) { + if (isInitialFolderSelect) { + targetIndex = 0; + } else if (isRandom) { if (wallpaperList.length > 1) { do { targetIndex = Math.floor(Math.random() * wallpaperList.length); @@ -331,6 +380,8 @@ Singleton { try { SessionData.setWallpaper(absolutePath); + SessionData.wallpaperCyclingFolderPath = ""; + SessionData.saveSettings(); return "SUCCESS: Wallpaper set to " + absolutePath; } catch (e) { return "ERROR: Failed to set wallpaper: " + e.toString(); @@ -341,6 +392,7 @@ Singleton { SessionData.setWallpaper(""); SessionData.setPerMonitorWallpaper(false); SessionData.monitorWallpapers = {}; + SessionData.wallpaperCyclingFolderPath = ""; SessionData.saveSettings(); return "SUCCESS: All wallpapers cleared"; } @@ -402,6 +454,7 @@ Singleton { SessionData.setPerMonitorWallpaper(true); } SessionData.setMonitorWallpaper(screenName, absolutePath); + SessionData.setMonitorCyclingFolderPath(screenName, ""); return "SUCCESS: Wallpaper set for " + screenName + " to " + absolutePath; } catch (e) { return "ERROR: Failed to set wallpaper for " + screenName + ": " + e.toString();