1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00
Files
DankMaterialShell/quickshell/Modals/Clipboard/ClipboardThumbnail.qml

160 lines
4.7 KiB
QML

import QtQuick
import QtQuick.Effects
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: thumbnail
required property var entry
required property string entryType
required property var modal
required property var listView
required property int itemIndex
Image {
id: thumbnailImage
property bool isVisible: false
property string cachedImageData: ""
property bool loadQueued: false
anchors.fill: parent
source: cachedImageData ? `data:image/png;base64,${cachedImageData}` : ""
fillMode: Image.PreserveAspectCrop
smooth: true
cache: false
visible: false
asynchronous: true
sourceSize.width: 128
sourceSize.height: 128
function tryLoadImage() {
if (loadQueued || entryType !== "image" || cachedImageData) {
return;
}
loadQueued = true;
if (modal.activeImageLoads < modal.maxConcurrentLoads) {
modal.activeImageLoads++;
loadImage();
} else {
retryTimer.restart();
}
}
function loadImage() {
DMSService.sendRequest("clipboard.getEntry", {
"id": entry.id
}, function (response) {
loadQueued = false;
if (modal.activeImageLoads > 0) {
modal.activeImageLoads--;
}
if (response.error) {
console.warn("ClipboardThumbnail: Failed to load image:", entry.id);
return;
}
const data = response.result?.data;
if (data) {
cachedImageData = data;
}
});
}
Timer {
id: retryTimer
interval: ClipboardConstants.retryInterval
onTriggered: {
if (!thumbnailImage.loadQueued) {
return;
}
if (modal.activeImageLoads < modal.maxConcurrentLoads) {
modal.activeImageLoads++;
thumbnailImage.loadImage();
} else {
retryTimer.restart();
}
}
}
Component.onCompleted: {
if (entryType !== "image") {
return;
}
const itemY = itemIndex * (ClipboardConstants.itemHeight + listView.spacing);
const viewTop = listView.contentY;
const viewBottom = viewTop + listView.height;
isVisible = (itemY + ClipboardConstants.itemHeight >= viewTop && itemY <= viewBottom);
if (isVisible) {
tryLoadImage();
}
}
Connections {
target: listView
function onContentYChanged() {
if (entryType !== "image") {
return;
}
const itemY = itemIndex * (ClipboardConstants.itemHeight + listView.spacing);
const viewTop = listView.contentY - ClipboardConstants.viewportBuffer;
const viewBottom = viewTop + listView.height + ClipboardConstants.extendedBuffer;
const nowVisible = (itemY + ClipboardConstants.itemHeight >= viewTop && itemY <= viewBottom);
if (nowVisible && !thumbnailImage.isVisible) {
thumbnailImage.isVisible = true;
thumbnailImage.tryLoadImage();
}
}
}
}
MultiEffect {
anchors.fill: parent
anchors.margins: 2
source: thumbnailImage
maskEnabled: true
maskSource: clipboardCircularMask
visible: entryType === "image" && thumbnailImage.status === Image.Ready && thumbnailImage.source != ""
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
Item {
id: clipboardCircularMask
width: ClipboardConstants.thumbnailSize - 4
height: ClipboardConstants.thumbnailSize - 4
layer.enabled: true
layer.smooth: true
visible: false
Rectangle {
anchors.fill: parent
radius: width / 2
color: "black"
antialiasing: true
}
}
DankIcon {
visible: !(entryType === "image" && thumbnailImage.status === Image.Ready && thumbnailImage.source != "")
name: {
switch (entryType) {
case "image":
return "image";
case "long_text":
return "subject";
default:
return "content_copy";
}
}
size: Theme.iconSize
color: Theme.primary
anchors.centerIn: parent
}
}