mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
160 lines
4.7 KiB
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
|
|
}
|
|
}
|