summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Widgets/CachingImage.qml
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Widgets/CachingImage.qml')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Widgets/CachingImage.qml102
1 files changed, 102 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Widgets/CachingImage.qml b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Widgets/CachingImage.qml
new file mode 100644
index 0000000..5ffec09
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Widgets/CachingImage.qml
@@ -0,0 +1,102 @@
+import QtQuick
+import qs.Common
+
+Item {
+ id: root
+
+ property string imagePath: ""
+ property int maxCacheSize: 512
+ property int status: isAnimated ? animatedImg.status : staticImg.status
+ property int fillMode: Image.PreserveAspectCrop
+
+ readonly property bool isRemoteUrl: imagePath.startsWith("http://") || imagePath.startsWith("https://")
+ readonly property bool isAnimated: {
+ if (!imagePath)
+ return false;
+ const lower = imagePath.toLowerCase();
+ return lower.endsWith(".gif") || lower.endsWith(".webp");
+ }
+ readonly property string normalizedPath: {
+ if (!imagePath)
+ return "";
+ if (isRemoteUrl)
+ return imagePath;
+ if (imagePath.startsWith("file://"))
+ return imagePath.substring(7);
+ return imagePath;
+ }
+
+ function djb2Hash(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');
+ }
+
+ readonly property string imageHash: normalizedPath ? djb2Hash(normalizedPath) : ""
+ readonly property string cachePath: imageHash && !isRemoteUrl && !isAnimated ? `${Paths.stringify(Paths.imagecache)}/${imageHash}@${maxCacheSize}x${maxCacheSize}.png` : ""
+ readonly property string encodedImagePath: {
+ if (!normalizedPath)
+ return "";
+ if (isRemoteUrl)
+ return normalizedPath;
+ return "file://" + normalizedPath.split('/').map(s => encodeURIComponent(s)).join('/');
+ }
+
+ AnimatedImage {
+ id: animatedImg
+ anchors.fill: parent
+ visible: root.isAnimated
+ asynchronous: true
+ fillMode: root.fillMode
+ source: root.isAnimated ? root.imagePath : ""
+ playing: visible && status === AnimatedImage.Ready
+ }
+
+ Image {
+ id: staticImg
+ anchors.fill: parent
+ visible: !root.isAnimated
+ asynchronous: true
+ fillMode: root.fillMode
+ sourceSize.width: root.maxCacheSize
+ sourceSize.height: root.maxCacheSize
+ smooth: true
+
+ onStatusChanged: {
+ if (source == root.cachePath && status === Image.Error) {
+ source = root.encodedImagePath;
+ return;
+ }
+ if (root.isRemoteUrl || source != root.encodedImagePath || status !== Image.Ready || !root.cachePath)
+ return;
+ Paths.mkdir(Paths.imagecache);
+ const grabPath = root.cachePath;
+ if (visible && width > 0 && height > 0 && Window.window?.visible) {
+ grabToImage(res => res.saveToFile(grabPath));
+ }
+ }
+ }
+
+ onImagePathChanged: {
+ if (!imagePath) {
+ staticImg.source = "";
+ return;
+ }
+ if (isAnimated)
+ return;
+ if (isRemoteUrl) {
+ staticImg.source = imagePath;
+ return;
+ }
+ Paths.mkdir(Paths.imagecache);
+ const hash = djb2Hash(normalizedPath);
+ const cPath = hash ? `${Paths.stringify(Paths.imagecache)}/${hash}@${maxCacheSize}x${maxCacheSize}.png` : "";
+ const encoded = "file://" + normalizedPath.split('/').map(s => encodeURIComponent(s)).join('/');
+ staticImg.source = cPath || encoded;
+ }
+}