1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-24 20:15:21 -04:00

feat(wallpaper): preload thumbnails & switch/sort options

- add sorting and clickable page jump
- wrap-around page navigation
This commit is contained in:
purian23
2026-06-23 21:39:06 -04:00
parent b2e728315b
commit 99cc3b8449
6 changed files with 448 additions and 41 deletions
+50
View File
@@ -0,0 +1,50 @@
import QtQuick
import QtQuick.Effects
import qs.Common
import qs.Services
// Frosted-glass backdrop: blurs the region of sourceItem directly behind the item
Item {
id: root
property Item sourceItem: null
property real radius: Theme.cornerRadius
property real blurAmount: 1.0
property int blurMax: 96
readonly property bool blurActive: visible && BlurService.enabled
ShaderEffectSource {
id: snapshot
anchors.fill: parent
sourceItem: root.sourceItem
sourceRect: {
if (!root.sourceItem)
return Qt.rect(0, 0, 0, 0);
const p = root.mapToItem(root.sourceItem, 0, 0);
return Qt.rect(p.x, p.y, root.width, root.height);
}
live: root.blurActive
hideSource: false
visible: false
}
MultiEffect {
anchors.fill: parent
source: snapshot
visible: root.blurActive
blurEnabled: root.blurActive
blurMax: root.blurMax
blur: root.blurAmount
maskEnabled: true
maskSource: maskRect
}
Rectangle {
id: maskRect
anchors.fill: parent
radius: root.radius
visible: false
layer.enabled: true
}
}
@@ -0,0 +1,94 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
// Preload the CachingImage disk cache for a folder of wallpapers via ffmpegthumbnailer
// so the switcher grid renders instantly. No-op (graceful fallback) if the tool is absent.
Item {
id: root
visible: false
property var paths: []
property int cacheSize: 256
property bool autoStart: true
property int maxConcurrent: 3
property int _active: 0
property var _queue: []
property int _toolState: -1 // -1 unknown, 0 unavailable, 1 available
onPathsChanged: if (autoStart)
preload()
// Must match djb2Hash + cachePath in Widgets/CachingImage.qml.
function _hash(str) {
if (!str)
return "";
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash) + str.charCodeAt(i);
hash = hash & 0x7FFFFFFF;
}
return hash.toString(16).padStart(8, '0');
}
function _cachePathFor(path) {
const hash = _hash(path);
if (!hash)
return "";
return `${Paths.stringify(Paths.imagecache)}/${hash}@${cacheSize}x${cacheSize}.png`;
}
function _isAnimated(path) {
const lower = path.toLowerCase();
return lower.endsWith(".gif") || lower.endsWith(".webp");
}
function preload() {
if (!paths || paths.length === 0 || _toolState === 0)
return;
if (_toolState === -1) {
Proc.runCommand("wallpaperThumbToolCheck", ["sh", "-c", "command -v ffmpegthumbnailer"], function (out, code) {
root._toolState = code === 0 ? 1 : 0;
if (root._toolState === 1)
root._start();
});
return;
}
_start();
}
function _start() {
Paths.mkdir(Paths.imagecache);
const q = [];
for (let i = 0; i < paths.length; i++) {
const p = paths[i];
if (!p || p.startsWith("#") || _isAnimated(p))
continue;
q.push(p);
}
_queue = q;
for (let i = 0; i < maxConcurrent; i++)
_pump();
}
function _pump() {
if (_queue.length === 0)
return;
const path = _queue.shift();
const cachePath = _cachePathFor(path);
if (!cachePath) {
_pump();
return;
}
_active++;
// One process per file: skip if already cached, otherwise generate.
const script = "test -f \"$1\" || ffmpegthumbnailer -i \"$2\" -o \"$1\" -s " + cacheSize;
Proc.runCommand(null, ["sh", "-c", script, "thumb", cachePath, path], function (out, code) {
root._active--;
root._pump();
}, 0, 20000);
}
}