From 8a1acb63c9b06a4eac276c96e7ac2a1f7de6bf7e Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 6 Jul 2026 18:15:49 -0400 Subject: [PATCH] launcher: improve clipboard preview performance related #2769 --- .../ClipboardLauncherPreview.qml | 11 ++++++-- .../Modals/DankLauncherV2/Controller.qml | 28 ++++++++++++++++--- .../DankLauncherV2/SpotlightResultRow.qml | 13 ++------- quickshell/Modals/DankLauncherV2/TileItem.qml | 3 ++ quickshell/Widgets/AppIconRenderer.qml | 1 + 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/quickshell/Modals/DankLauncherV2/ClipboardLauncherPreview.qml b/quickshell/Modals/DankLauncherV2/ClipboardLauncherPreview.qml index 56b9fae87..4e0007276 100644 --- a/quickshell/Modals/DankLauncherV2/ClipboardLauncherPreview.qml +++ b/quickshell/Modals/DankLauncherV2/ClipboardLauncherPreview.qml @@ -41,13 +41,18 @@ Rectangle { } function reloadPreview() { - cachedImageData = ""; - cachedMimeType = ""; if (!canLoadImage || !entry?.id) { _requestedEntryId = null; + cachedImageData = ""; + cachedMimeType = ""; return; } + // Entry objects are rebuilt per search; same id means same content + if (entry.id === _requestedEntryId) + return; + cachedImageData = ""; + cachedMimeType = ""; const entryId = entry.id; _requestedEntryId = entryId; DMSService.sendRequest("clipboard.getEntry", { @@ -78,6 +83,8 @@ Rectangle { asynchronous: true cache: false smooth: true + sourceSize.width: 128 + sourceSize.height: 128 fillMode: Image.PreserveAspectCrop visible: status === Image.Ready } diff --git a/quickshell/Modals/DankLauncherV2/Controller.qml b/quickshell/Modals/DankLauncherV2/Controller.qml index b6782e58e..e2ef918a0 100644 --- a/quickshell/Modals/DankLauncherV2/Controller.qml +++ b/quickshell/Modals/DankLauncherV2/Controller.qml @@ -110,7 +110,7 @@ Item { if (query !== effectiveQuery) return; - searchDebounce.restart(); + root.requestSearch(); } } @@ -380,10 +380,29 @@ Item { property bool _pluginPhaseForceFirst: false property var _phase1Items: [] + property bool _searchPending: false + + // Leading-edge debounce: search immediately when idle, coalesce bursts + function requestSearch() { + if (searchDebounce.running) { + _searchPending = true; + searchDebounce.restart(); + return; + } + _searchPending = false; + performSearch(); + searchDebounce.restart(); + } + Timer { id: searchDebounce interval: 60 - onTriggered: root.performSearch() + onTriggered: { + if (!root._searchPending) + return; + root._searchPending = false; + root.performSearch(); + } } Timer { @@ -409,7 +428,7 @@ Item { _phase1Items = []; pluginPhaseTimer.stop(); searchQuery = query; - searchDebounce.restart(); + requestSearch(); if (searchMode !== "plugins" && query.startsWith("/")) { var prefix = Utils.parseFileSearchPrefix(query); @@ -1871,7 +1890,8 @@ Item { } function executeSelected() { - if (searchDebounce.running) { + if (_searchPending) { + _searchPending = false; searchDebounce.stop(); performSearch(); } diff --git a/quickshell/Modals/DankLauncherV2/SpotlightResultRow.qml b/quickshell/Modals/DankLauncherV2/SpotlightResultRow.qml index 45da2b929..7bc57600f 100644 --- a/quickshell/Modals/DankLauncherV2/SpotlightResultRow.qml +++ b/quickshell/Modals/DankLauncherV2/SpotlightResultRow.qml @@ -50,7 +50,6 @@ Rectangle { } readonly property bool hasClipboardPreview: item?.type === "clipboard" && item?.data?.isImage === true && (item?.data?.mimeType ?? "").startsWith("image/") readonly property bool hasMediaPreview: previewSource.length > 0 || hasClipboardPreview - readonly property bool previewAnimated: previewSource.toLowerCase().indexOf(".gif") >= 0 readonly property string typeLabel: { if (!item) @@ -284,16 +283,10 @@ Rectangle { anchors.fill: parent source: root.previewSource asynchronous: true + sourceSize.width: 128 + sourceSize.height: 128 fillMode: Image.PreserveAspectCrop - visible: !root.hasClipboardPreview && !root.previewAnimated - } - - AnimatedImage { - anchors.fill: parent - source: root.previewSource - fillMode: Image.PreserveAspectCrop - playing: visible - visible: !root.hasClipboardPreview && root.previewAnimated + visible: !root.hasClipboardPreview } ClipboardLauncherPreview { diff --git a/quickshell/Modals/DankLauncherV2/TileItem.qml b/quickshell/Modals/DankLauncherV2/TileItem.qml index 6ca6639f8..1f148e129 100644 --- a/quickshell/Modals/DankLauncherV2/TileItem.qml +++ b/quickshell/Modals/DankLauncherV2/TileItem.qml @@ -165,6 +165,9 @@ Rectangle { anchors.margins: root.hasScreencopy ? 4 : 0 fillMode: Image.PreserveAspectFit source: root.item?.data?.attribution || "" + asynchronous: true + sourceSize.width: 80 + sourceSize.height: 80 mipmap: true } } diff --git a/quickshell/Widgets/AppIconRenderer.qml b/quickshell/Widgets/AppIconRenderer.qml index ccd10a83f..8880e6497 100644 --- a/quickshell/Widgets/AppIconRenderer.qml +++ b/quickshell/Widgets/AppIconRenderer.qml @@ -85,6 +85,7 @@ Item { anchors.fill: parent imagePath: root.imagePath maxCacheSize: root.iconSize * 2 + animate: false visible: root.isImage && status === Image.Ready }