diff options
Diffstat (limited to 'appearance/Wallpapers.qml')
| -rw-r--r-- | appearance/Wallpapers.qml | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/appearance/Wallpapers.qml b/appearance/Wallpapers.qml new file mode 100644 index 0000000..0d61f90 --- /dev/null +++ b/appearance/Wallpapers.qml @@ -0,0 +1,115 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +pragma Singleton + +import Quickshell +import Quickshell.Io +import QtQuick + +// The wallpaper side. Setting one is `wallp`'s job: it knows about the two +// outputs, the saved state and the accent run, and none of that is worth +// reimplementing here. +Singleton { + id: root + + readonly property string home: Quickshell.env("HOME") + readonly property string dir: `${home}/Pictures/wallpapers` + + // Which screen a pick applies to. wallp calls them H and V. + property string target: "H" + + // Staged picks, empty until something is chosen. Selecting does not set + // anything: both screens can be composed and applied together, which is + // also a single wallp call rather than two. + property string pendingH: "" + property string pendingV: "" + readonly property bool dirty: pendingH !== "" || pendingV !== "" + + function pick(path) { + if (target === "V") pendingV = path; + else pendingH = path; + } + + function clearPending() { pendingH = ""; pendingV = ""; } + + // What a screen should show: the staged pick if there is one, else what + // is actually set. + function shown(which) { + if (which === "V") return pendingV !== "" ? pendingV : currentV; + return pendingH !== "" ? pendingH : currentH; + } + + property list<string> files: [] + property bool scanning: true + property bool applying: false + signal applied(bool ok, string message) + + // Sorted by most recent first: the wallpaper wanted now is usually one + // added recently. -maxdepth keeps a stray git checkout from being walked. + Process { + running: true + command: ["sh", "-c", + `find ${JSON.stringify(root.dir)} -maxdepth 2 -type f ` + + `\\( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.webp' \\) ` + + `-not -path '*/.*' -printf '%T@ %p\\n' 2>/dev/null | sort -rn | cut -d' ' -f2-`] + stdout: StdioCollector { + onStreamFinished: { + root.files = text.trim().split("\n").filter(s => s.length); + root.scanning = false; + } + } + } + + function basename(path) { return path.slice(path.lastIndexOf("/") + 1); } + + // What is on each screen now. wallp writes these on every set, so + // watching them keeps the mock honest even when set from a terminal. + property string currentH: "" + property string currentV: "" + + FileView { + path: `${root.home}/.config/wallp/wall_h` + watchChanges: true + onFileChanged: reload() + onLoaded: root.currentH = text().trim() + } + + FileView { + path: `${root.home}/.config/wallp/wall_v` + watchChanges: true + onFileChanged: reload() + onLoaded: root.currentV = text().trim() + } + + // wallp does the work, including running udt-accent, so the accent follows + // the new wallpaper exactly as it does from the command line. Both screens + // go in one call: wallp accepts H= and V= together. + function apply() { + if (applying || !dirty) return; + applying = true; + const args = []; + if (pendingH !== "") args.push(`H=${JSON.stringify(pendingH)}`); + if (pendingV !== "") args.push(`V=${JSON.stringify(pendingV)}`); + applyProc.command = ["sh", "-c", `wallp --set ${args.join(" ")} >/dev/null 2>&1`]; + applyProc.running = true; + } + + Process { + id: applyProc + stderr: StdioCollector { id: applyErr } + onExited: code => { + root.applying = false; + if (code === 0) root.clearPending(); + root.applied(code === 0, code === 0 ? "" : (applyErr.text.trim() || `wallp exited ${code}`)); + } + } +} |
