feat(wallpaper): add folder-based wallpaper selection and shuffle (#2990)

* feat(wallpaper): add folder-based wallpaper selection and shuffle

* fix(wallpaper): bump 'dank-qml-common' and handle single-wallpaper folders

* fix(wallpaper): address review feedback for folder selection

* fix(wallpaper): persist folder selection across restarts

* fix(wallpaper): pick first wallpaper on folder select and persist folder path

* fix: resolve wallpaper cycling folder persistence issues
This commit is contained in:
0xNA
2026-08-21 19:09:13 -04:00
committed by GitHub
parent a669253c58
commit d22dae1494
6 changed files with 150 additions and 7 deletions
+34 -1
View File
@@ -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 : {});
@@ -24,6 +24,7 @@ var SPEC = {
wallpaperCyclingMode: { def: "interval" },
wallpaperCyclingInterval: { def: 300 },
wallpaperCyclingTime: { def: "06:00" },
wallpaperCyclingFolderPath: { def: "" },
monitorCyclingSettings: { def: {} },
nightModeEnabled: { def: false },
@@ -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: () => {
@@ -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();
}
}
@@ -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
@@ -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();