wallpaper selector: thumbnail generation, fix xdg dir folder icons

This commit is contained in:
end-4 2025-08-24 18:59:41 +07:00
parent f3ab3573c3
commit b2d14ca101
4 changed files with 73 additions and 25 deletions

View file

@ -0,0 +1,67 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.modules.common
import qs.modules.common.functions
/**
* Thumbnail image.
* See Freedesktop's spec: https://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html
*/
Image {
id: root
required property string sourcePath
readonly property var thumbnailSizes: ({
"normal": 128,
"large": 256,
"x-large": 512,
"xx-large": 1024
})
property string thumbnailSizeName: { // https://specifications.freedesktop.org/thumbnail-spec/latest/directory.html
const sizeNames = Object.keys(thumbnailSizes);
for(let i = 0; i < sizeNames.length; i++) {
const sizeName = sizeNames[i];
const maxSize = thumbnailSizes[sizeName];
if (root.sourceSize.width <= maxSize && root.sourceSize.height <= maxSize) return sizeName;
}
return "xx-large";
}
property string thumbnailPath: {
if (sourcePath.length == 0) return;
const resolvedUrl = Qt.resolvedUrl(sourcePath);
const md5Hash = Qt.md5(resolvedUrl);
return `${Directories.genericCache}/thumbnails/${thumbnailSizeName}/${md5Hash}.png`;
}
source: thumbnailPath
asynchronous: true
cache: false
smooth: true
mipmap: false
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
onSourceSizeChanged: {
thumbnailGeneration.running = false
thumbnailGeneration.running = true
}
Process {
id: thumbnailGeneration
command: {
const maxSize = root.thumbnailSizes[root.thumbnailSizeName];
return ["bash", "-c",
`[ -f '${FileUtils.trimFileProtocol(root.thumbnailPath)}' ] && exit 0 || { magick '${root.sourcePath}' -resize ${maxSize}x${maxSize} '${FileUtils.trimFileProtocol(root.thumbnailPath)}' && exit 1; }`
]
}
onExited: (exitCode, exitStatus) => {
if (exitCode === 1) { // Force reload if thumbnail had to be generated
root.source = "";
root.source = root.thumbnailPath; // Force reload
}
}
}
}

View file

@ -2,6 +2,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.modules.common
import qs.modules.common.functions
// From https://github.com/caelestia-dots/shell with modifications.
// License: GPLv3
@ -16,8 +17,7 @@ Image {
if (!fileModelData.fileIsDir)
return Quickshell.iconPath("application-x-zerosize");
const homeDir = Directories.home
if ([Directories.documents, Directories.downloads, Directories.music, Directories.pictures, Directories.videos].includes(fileModelData.filePath))
if ([Directories.documents, Directories.downloads, Directories.music, Directories.pictures, Directories.videos].some(dir => FileUtils.trimFileProtocol(dir) === fileModelData.filePath))
return Quickshell.iconPath(`folder-${fileModelData.fileName.toLowerCase()}`);
return Quickshell.iconPath("inode-directory");

View file

@ -65,34 +65,15 @@ MouseArea {
id: thumbnailImageLoader
anchors.fill: parent
active: root.useThumbnail
sourceComponent: Image {
sourceComponent: ThumbnailImage {
id: thumbnailImage
source: {
if (fileModelData.filePath.length == 0)
return;
const resolvedUrl = Qt.resolvedUrl(fileModelData.filePath);
const md5Hash = Qt.md5(resolvedUrl);
const cacheSize = "normal";
const thumbnailPath = `${Directories.genericCache}/thumbnails/${cacheSize}/${md5Hash}.png`;
return thumbnailPath;
}
asynchronous: true
cache: false
smooth: true
mipmap: false
sourcePath: fileModelData.filePath
fillMode: Image.PreserveAspectCrop
clip: true
sourceSize.width: wallpaperItemColumnLayout.width
sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
onStatusChanged: if (status === Image.Error)
root.useThumbnail = false
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {

View file

@ -226,8 +226,8 @@ Item {
fileModelData: modelData
width: grid.cellWidth
height: grid.cellHeight
colBackground: (index === grid?.currentIndex || containsMouse) ? Appearance.colors.colPrimaryContainer : ColorUtils.transparentize(Appearance.colors.colPrimaryContainer)
colText: (index === grid.currentIndex || containsMouse) ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer0
colBackground: (fileModelData.filePath === Config.options.background.wallpaperPath) ? Appearance.colors.colPrimary : (index === grid?.currentIndex || containsMouse) ? Appearance.colors.colPrimaryContainer : ColorUtils.transparentize(Appearance.colors.colPrimaryContainer)
colText: (fileModelData.filePath === Config.options.background.wallpaperPath) ? Appearance.colors.colOnPrimary : (index === grid.currentIndex || containsMouse) ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer0
onEntered: {
grid.currentIndex = index;