summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/Clipboard/ClipboardThumbnail.qml
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/Clipboard/ClipboardThumbnail.qml')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/Clipboard/ClipboardThumbnail.qml178
1 files changed, 178 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/Clipboard/ClipboardThumbnail.qml b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/Clipboard/ClipboardThumbnail.qml
new file mode 100644
index 0000000..26a9210
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/Clipboard/ClipboardThumbnail.qml
@@ -0,0 +1,178 @@
+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 (thumbnailImage.loadQueued || entryType !== "image" || thumbnailImage.cachedImageData) {
+ return;
+ }
+ thumbnailImage.loadQueued = true;
+ if (modal.activeImageLoads < modal.maxConcurrentLoads) {
+ modal.activeImageLoads++;
+ thumbnailImage.loadImage();
+ } else {
+ retryTimer.restart();
+ }
+ }
+
+ function loadImage() {
+ DMSService.sendRequest("clipboard.getEntry", {
+ "id": entry.id
+ }, function (response) {
+ thumbnailImage.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) {
+ thumbnailImage.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" || listView.height <= 0) {
+ 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();
+ }
+ }
+
+ Timer {
+ id: visibilityTimer
+ interval: 100
+ onTriggered: thumbnailImage.checkVisibility()
+ }
+
+ function checkVisibility() {
+ if (entryType !== "image" || listView.height <= 0 || isVisible) {
+ 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) {
+ isVisible = true;
+ tryLoadImage();
+ }
+ }
+
+ Connections {
+ target: listView
+
+ function onContentYChanged() {
+ if (thumbnailImage.isVisible || entryType !== "image") {
+ return;
+ }
+ visibilityTimer.restart();
+ }
+
+ function onHeightChanged() {
+ if (thumbnailImage.isVisible || entryType !== "image") {
+ return;
+ }
+ visibilityTimer.restart();
+ }
+ }
+ }
+
+ MultiEffect {
+ anchors.fill: parent
+ anchors.margins: 2
+ source: thumbnailImage
+ maskEnabled: true
+ maskSource: clipboardRoundedRectangularMask
+ visible: entryType === "image" && thumbnailImage.status === Image.Ready && thumbnailImage.source != ""
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 1
+ }
+
+ Item {
+ id: clipboardRoundedRectangularMask
+ width: ClipboardConstants.thumbnailSize
+ height: ClipboardConstants.itemHeight - 4
+ layer.enabled: true
+ layer.smooth: true
+ visible: false
+
+ Rectangle {
+ anchors.fill: parent
+ radius: Theme.cornerRadius / 2 // Thumbnail corner radius is divided by 2 so it doesnt look weird on large corner radius (eg: 32px)
+ 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
+ }
+}