Wallpapers: added thumbnail caching. Had to drop the rounded borders for it to work.

NImageCached relies on the image being visible to work properly.
MultiEffect relies on the image being invisible to apply effects.
That's why we don't have rounded corners here, as we don't want to bring
back qt5compat.
This commit is contained in:
quadbyte 2025-08-17 17:48:39 -04:00
parent 7509bbd363
commit 89e359015e
3 changed files with 78 additions and 12 deletions

View file

@ -17,6 +17,7 @@ Singleton {
"HOME") + "/.config") + "/" + shellName + "/"
property string cacheDir: Quickshell.env("NOCTALIA_CACHE_DIR") || (Quickshell.env("XDG_CACHE_HOME") || Quickshell.env(
"HOME") + "/.cache") + "/" + shellName + "/"
property string cacheDirImages: cacheDir + "images/"
property string settingsFile: Quickshell.env("NOCTALIA_SETTINGS_FILE") || (configDir + "settings.json")
@ -37,6 +38,7 @@ Singleton {
// ensure settings dir exists
Quickshell.execDetached(["mkdir", "-p", configDir])
Quickshell.execDetached(["mkdir", "-p", cacheDir])
Quickshell.execDetached(["mkdir", "-p", cacheDirImages])
}
}

View file

@ -135,27 +135,32 @@ Item {
delegate: Rectangle {
id: wallpaperItem
property string wallpaperPath: modelData
property bool isSelected: wallpaperPath === WallpaperService.currentWallpaper
width: wallpaperGridView.itemSize
height: Math.floor(wallpaperGridView.itemSize * 0.67)
radius: Style.radiusMedium * scaling
color: isSelected ? Color.mPrimary : Color.mSurface
border.color: isSelected ? Color.mSecondary : Color.mOutline
border.width: Math.max(1, Style.borderThin * scaling)
clip: true
color: Color.transparent
NImageRounded {
anchors.fill: parent
anchors.margins: Style.marginTiny * scaling
// NImageCached relies on the image being visible to work properly.
// MultiEffect relies on the image being invisible to apply effects.
// That's why we don't have rounded corners here, as we don't want to bring back qt5compat.
NImageCached {
id: img
imagePath: wallpaperPath
fallbackIcon: "image"
imageRadius: Style.radiusMedium * scaling
anchors.fill: parent
}
// Selection indicator
// Borders on top
Rectangle {
anchors.fill: parent
color: Color.transparent
border.color: isSelected ? Color.mPrimary : Color.mOutline
border.width: Math.max(1, Style.borderThick * scaling)
}
// Selection tick-mark
Rectangle {
anchors.top: parent.top
anchors.right: parent.right

59
Widgets/NImageCached.qml Normal file
View file

@ -0,0 +1,59 @@
pragma ComponentBehavior
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
Image {
id: root
property string imagePath: ""
property string imageHash: ""
property int maxCacheDimension: 512
readonly property string cachePath: imageHash ? `${Settings.cacheDirImages}${imageHash}@${maxCacheDimension}x${maxCacheDimension}.png` : ""
asynchronous: true
fillMode: Image.PreserveAspectCrop
sourceSize.width: maxCacheDimension
sourceSize.height: maxCacheDimension
smooth: true
onImagePathChanged: {
if (imagePath) {
hashProcess.command = ["sha256sum", imagePath]
hashProcess.running = true
} else {
source = ""
imageHash = ""
}
}
onCachePathChanged: {
if (imageHash && cachePath) {
// Try to load the cached version, failure will be detected below in onStatusChanged
source = cachePath
//Logger.Log(imagePath, cachePath)
}
}
onStatusChanged: {
if (source == cachePath && status === Image.Error) {
// Cached image was not available, show the original
source = imagePath
} else if (source == imagePath && status === Image.Ready && imageHash && cachePath) {
// Original image is shown and fully loaded, time to cache it
const grabPath = cachePath
if (visible && width > 0 && height > 0 && Window.window && Window.window.visible)
grabToImage(res => {
return res.saveToFile(grabPath)
})
}
}
Process {
id: hashProcess
stdout: StdioCollector {
onStreamFinished: {
root.imageHash = text.split(" ")[0]
}
}
}
}