forked from Shinonome/dots-hyprland
wallpaper selector: thumbnail generation, fix xdg dir folder icons
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import QtQuick
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
|
||||||
// From https://github.com/caelestia-dots/shell with modifications.
|
// From https://github.com/caelestia-dots/shell with modifications.
|
||||||
// License: GPLv3
|
// License: GPLv3
|
||||||
@@ -16,8 +17,7 @@ Image {
|
|||||||
if (!fileModelData.fileIsDir)
|
if (!fileModelData.fileIsDir)
|
||||||
return Quickshell.iconPath("application-x-zerosize");
|
return Quickshell.iconPath("application-x-zerosize");
|
||||||
|
|
||||||
const homeDir = Directories.home
|
if ([Directories.documents, Directories.downloads, Directories.music, Directories.pictures, Directories.videos].some(dir => FileUtils.trimFileProtocol(dir) === fileModelData.filePath))
|
||||||
if ([Directories.documents, Directories.downloads, Directories.music, Directories.pictures, Directories.videos].includes(fileModelData.filePath))
|
|
||||||
return Quickshell.iconPath(`folder-${fileModelData.fileName.toLowerCase()}`);
|
return Quickshell.iconPath(`folder-${fileModelData.fileName.toLowerCase()}`);
|
||||||
|
|
||||||
return Quickshell.iconPath("inode-directory");
|
return Quickshell.iconPath("inode-directory");
|
||||||
|
|||||||
@@ -65,34 +65,15 @@ MouseArea {
|
|||||||
id: thumbnailImageLoader
|
id: thumbnailImageLoader
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.useThumbnail
|
active: root.useThumbnail
|
||||||
sourceComponent: Image {
|
sourceComponent: ThumbnailImage {
|
||||||
id: thumbnailImage
|
id: thumbnailImage
|
||||||
source: {
|
sourcePath: fileModelData.filePath
|
||||||
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
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
clip: true
|
clip: true
|
||||||
sourceSize.width: wallpaperItemColumnLayout.width
|
sourceSize.width: wallpaperItemColumnLayout.width
|
||||||
sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height
|
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.enabled: true
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
maskSource: Rectangle {
|
maskSource: Rectangle {
|
||||||
|
|||||||
@@ -226,8 +226,8 @@ Item {
|
|||||||
fileModelData: modelData
|
fileModelData: modelData
|
||||||
width: grid.cellWidth
|
width: grid.cellWidth
|
||||||
height: grid.cellHeight
|
height: grid.cellHeight
|
||||||
colBackground: (index === grid?.currentIndex || containsMouse) ? Appearance.colors.colPrimaryContainer : ColorUtils.transparentize(Appearance.colors.colPrimaryContainer)
|
colBackground: (fileModelData.filePath === Config.options.background.wallpaperPath) ? Appearance.colors.colPrimary : (index === grid?.currentIndex || containsMouse) ? Appearance.colors.colPrimaryContainer : ColorUtils.transparentize(Appearance.colors.colPrimaryContainer)
|
||||||
colText: (index === grid.currentIndex || containsMouse) ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer0
|
colText: (fileModelData.filePath === Config.options.background.wallpaperPath) ? Appearance.colors.colOnPrimary : (index === grid.currentIndex || containsMouse) ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer0
|
||||||
|
|
||||||
onEntered: {
|
onEntered: {
|
||||||
grid.currentIndex = index;
|
grid.currentIndex = index;
|
||||||
|
|||||||
Reference in New Issue
Block a user