From adaa5e2bb046a968d61a7bfdb49d765b0f9e11ec Mon Sep 17 00:00:00 2001 From: purian23 Date: Sun, 11 Oct 2026 03:39:24 -0400 Subject: [PATCH] fix(clipboard): multi-second tab switch bind & stale pinned count - qml common fix & update --- dank-qml-common | 2 +- flake.lock | 6 +- .../Modals/Clipboard/ClipboardContent.qml | 18 ++++- .../Clipboard/ClipboardHistoryModal.qml | 1 + .../Clipboard/ClipboardHistoryPopout.qml | 1 + quickshell/Services/ClipboardService.qml | 75 +++++++------------ 6 files changed, 46 insertions(+), 57 deletions(-) diff --git a/dank-qml-common b/dank-qml-common index 50ca5ccb4..0e6a5162d 160000 --- a/dank-qml-common +++ b/dank-qml-common @@ -1 +1 @@ -Subproject commit 50ca5ccb4913098b0b67807d9ff7f642e79067b0 +Subproject commit 0e6a5162d34abaae4542e83b0751318990a75fe0 diff --git a/flake.lock b/flake.lock index e693f44bc..886106e58 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "dank-qml-common": { "flake": false, "locked": { - "lastModified": 1791670308, - "narHash": "sha256-vcAOSK+6buTddG8sJi8JOnaFfxSztmL8nOzsnUTzGd0=", + "lastModified": 1791704080, + "narHash": "sha256-zcbXHrade9CzEgLU1Alc5sCjUPv+m5GLSYtAurU+WV8=", "owner": "AvengeMedia", "repo": "dank-qml-common", - "rev": "50ca5ccb4913098b0b67807d9ff7f642e79067b0", + "rev": "0e6a5162d34abaae4542e83b0751318990a75fe0", "type": "github" }, "original": { diff --git a/quickshell/Modals/Clipboard/ClipboardContent.qml b/quickshell/Modals/Clipboard/ClipboardContent.qml index dbe980543..7eb216909 100644 --- a/quickshell/Modals/Clipboard/ClipboardContent.qml +++ b/quickshell/Modals/Clipboard/ClipboardContent.qml @@ -92,6 +92,18 @@ Item { } } + ScriptModel { + id: savedModel + values: clipboardContent.modal.pinnedEntries + objectProp: "id" + } + + ScriptModel { + id: recentsModel + values: clipboardContent.modal.unpinnedEntries + objectProp: "id" + } + ClipboardContextMenu { id: contextMenu modal: clipboardContent.modal @@ -223,10 +235,8 @@ Item { reuseItems: true highlightSelection: clipboardContent.modal.keyboardNavigationActive && clipboardContent.modal.selectedIndex >= 0 anchors.fill: parent - model: ScriptModel { - values: clipboardContent.entries - objectProp: "id" - } + // one model per tab; a shared model diffs the whole list on every tab switch + model: clipboardContent.modal.activeTab === "saved" ? savedModel : recentsModel currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0 spacing: Theme.groupedListGap diff --git a/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml b/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml index ae71cae80..c5810b448 100644 --- a/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml +++ b/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml @@ -87,6 +87,7 @@ DModal { Ref { service: ClipboardService + active: clipboardHistoryModal.shouldBeVisible } ConfirmModal { diff --git a/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml b/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml index c9e330f35..888437d13 100644 --- a/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml +++ b/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml @@ -98,6 +98,7 @@ DPopout { Ref { service: ClipboardService + active: root.shouldBeVisible } ConfirmModal { diff --git a/quickshell/Services/ClipboardService.qml b/quickshell/Services/ClipboardService.qml index 13f49e49f..630a68312 100644 --- a/quickshell/Services/ClipboardService.qml +++ b/quickshell/Services/ClipboardService.qml @@ -56,31 +56,17 @@ Singleton { Connections { target: DMSService - enabled: root.refCount > 0 function onIsConnectedChanged() { root.refreshPasteSupport(); } } - onRefCountChanged: { - if (refCount > 0) { - ensureSubscription(); - } else if (refCount === 0 && DMSService.activeSubscriptions.includes("clipboard")) { - DMSService.removeSubscription("clipboard"); - } - } + // subscribed for the shell's lifetime; resubscribing replays every service's snapshot + Component.onCompleted: DMSService.addSubscription("clipboard") - function ensureSubscription() { - if (refCount <= 0) - return; - if (!_pasteSupportChecked) + onRefCountChanged: { + if (refCount > 0 && !_pasteSupportChecked) refreshPasteSupport(); - if (DMSService.activeSubscriptions.includes("clipboard")) - return; - if (DMSService.activeSubscriptions.includes("all")) - return; - DMSService.addSubscription("clipboard"); - refresh(); } function refreshPasteSupport() { @@ -163,13 +149,16 @@ Singleton { log.warn("Failed to get history:", response.error); return; } - internalEntries = response.result || []; - pinnedEntries = internalEntries.filter(e => e.pinned); - pinnedCount = pinnedEntries.length; - updateFilteredModel(); + applyHistory(response.result || []); }); } + function applyHistory(history) { + internalEntries = history; + pinnedCount = history.filter(e => e.pinned).length; + updateFilteredModel(); + } + function requestLauncherSearch(query, limit) { if (!clipboardAvailable) { return; @@ -333,28 +322,16 @@ Singleton { } function pinEntry(entry) { - DMSService.sendRequest("clipboard.getPinnedCount", null, function (countResponse) { - if (countResponse.error) { - ToastService.showError(I18n.tr("Failed to check pin limit")); + DMSService.sendRequest("clipboard.pinEntry", { + "id": entry.id + }, function (response) { + if (response.error) { + // error text from core/internal/server/clipboard + const limit = /^maximum pinned entries reached \((\d+)\)/.exec(response.error); + ToastService.showError(limit ? I18n.tr("Maximum pinned entries reached") + " (" + limit[1] + ")" : I18n.tr("Failed to pin entry")); return; } - - const maxPinned = 25; - if (countResponse.result.count >= maxPinned) { - ToastService.showError(I18n.tr("Maximum pinned entries reached") + " (" + maxPinned + ")"); - return; - } - - DMSService.sendRequest("clipboard.pinEntry", { - "id": entry.id - }, function (response) { - if (response.error) { - ToastService.showError(I18n.tr("Failed to pin entry")); - return; - } - ToastService.showInfo(I18n.tr("Entry pinned")); - refresh(); - }); + ToastService.showInfo(I18n.tr("Entry pinned")); }); } @@ -367,7 +344,6 @@ Singleton { return; } ToastService.showInfo(I18n.tr("Entry unpinned")); - refresh(); }); } @@ -492,15 +468,16 @@ Singleton { refresh(); } + // only pinnedCount is tracked while no surface is open; the bar menu reads it, surfaces refresh on open Connections { target: DMSService - enabled: root.refCount > 0 function onClipboardStateUpdate(data) { - const newHistory = data.history || []; - internalEntries = newHistory; - pinnedEntries = newHistory.filter(e => e.pinned); - pinnedCount = pinnedEntries.length; - updateFilteredModel(); + const history = data.history || []; + if (root.refCount > 0) { + root.applyHistory(history); + return; + } + root.pinnedCount = history.filter(e => e.pinned).length; } } }