From 12a744e985a0e051e1c75f17bea7d1fe7be3484f Mon Sep 17 00:00:00 2001 From: bbedward Date: Tue, 26 May 2026 11:49:01 -0400 Subject: [PATCH] clipboard: fix editing in popout --- .../Modals/Clipboard/ClipboardContent.qml | 9 - .../Modals/Clipboard/ClipboardEntry.qml | 5 +- .../Clipboard/ClipboardHistoryContent.qml | 210 +++++++++++++++ .../Clipboard/ClipboardHistoryModal.qml | 241 ++---------------- .../Clipboard/ClipboardHistoryPopout.qml | 134 ++-------- 5 files changed, 256 insertions(+), 343 deletions(-) create mode 100644 quickshell/Modals/Clipboard/ClipboardHistoryContent.qml diff --git a/quickshell/Modals/Clipboard/ClipboardContent.qml b/quickshell/Modals/Clipboard/ClipboardContent.qml index a4599b10..9c6ed8f4 100644 --- a/quickshell/Modals/Clipboard/ClipboardContent.qml +++ b/quickshell/Modals/Clipboard/ClipboardContent.qml @@ -65,15 +65,6 @@ Item { forceActiveFocus(); }); } - - Connections { - target: modal - function onOpened() { - Qt.callLater(function () { - searchField.forceActiveFocus(); - }); - } - } } } diff --git a/quickshell/Modals/Clipboard/ClipboardEntry.qml b/quickshell/Modals/Clipboard/ClipboardEntry.qml index d0383ff9..5f57bc48 100644 --- a/quickshell/Modals/Clipboard/ClipboardEntry.qml +++ b/quickshell/Modals/Clipboard/ClipboardEntry.qml @@ -78,10 +78,9 @@ Rectangle { onClicked: { if (entryType === "image") { - // TODO - forward to editing software - } else { - editRequested(); + return; } + editRequested(); } } diff --git a/quickshell/Modals/Clipboard/ClipboardHistoryContent.qml b/quickshell/Modals/Clipboard/ClipboardHistoryContent.qml new file mode 100644 index 00000000..d7c89c49 --- /dev/null +++ b/quickshell/Modals/Clipboard/ClipboardHistoryContent.qml @@ -0,0 +1,210 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import qs.Common +import qs.Services + +FocusScope { + id: root + + property var clearConfirmDialog: null + + property string activeTab: "recents" + property bool showKeyboardHints: false + property int activeImageLoads: 0 + readonly property int maxConcurrentLoads: 3 + + property string mode: "history" + property string searchText: ClipboardService.searchText + + readonly property bool clipboardAvailable: ClipboardService.clipboardAvailable + readonly property bool wtypeAvailable: ClipboardService.wtypeAvailable + readonly property int totalCount: ClipboardService.totalCount + readonly property var clipboardEntries: ClipboardService.clipboardEntries + readonly property var pinnedEntries: ClipboardService.pinnedEntries + readonly property int pinnedCount: ClipboardService.pinnedCount + readonly property var unpinnedEntries: ClipboardService.unpinnedEntries + readonly property int selectedIndex: ClipboardService.selectedIndex + readonly property bool keyboardNavigationActive: ClipboardService.keyboardNavigationActive + + readonly property var modalFocusScope: root + property alias searchField: historyContent.searchField + property alias editorView: editorView + property alias keyboardController: keyboardController + + signal closeRequested + signal instantCloseRequested + + onActiveTabChanged: { + ClipboardService.selectedIndex = 0; + ClipboardService.keyboardNavigationActive = false; + } + onSearchTextChanged: ClipboardService.searchText = searchText + + function hide() { + closeRequested(); + } + + function pasteSelected() { + ClipboardService.pasteSelected(() => root.instantCloseRequested()); + } + + function copyEntry(entry) { + ClipboardService.copyEntry(entry, () => root.closeRequested()); + } + + function deleteEntry(entry) { + ClipboardService.deleteEntry(entry); + } + + function deletePinnedEntry(entry) { + ClipboardService.deletePinnedEntry(entry, clearConfirmDialog); + } + + function pinEntry(entry) { + ClipboardService.pinEntry(entry); + } + + function unpinEntry(entry) { + ClipboardService.unpinEntry(entry); + } + + function clearAll() { + ClipboardService.clearAll(); + } + + function getEntryPreview(entry) { + return ClipboardService.getEntryPreview(entry); + } + + function getEntryType(entry) { + return ClipboardService.getEntryType(entry); + } + + function updateFilteredModel() { + ClipboardService.updateFilteredModel(); + } + + function refreshClipboard() { + ClipboardService.refresh(); + } + + function editEntry(entry) { + if (!entry || entry.isImage) { + return; + } + editorView.setEntry(entry); + mode = "editor"; + } + + function resetState() { + activeImageLoads = 0; + mode = "history"; + ClipboardService.reset(); + keyboardController.reset(); + } + + focus: true + Keys.onPressed: function (event) { + keyboardController.handleKey(event); + } + + ClipboardKeyboardController { + id: keyboardController + modal: root + } + + Item { + id: historyView + anchors.fill: parent + opacity: 1 + scale: 1 + visible: opacity > 0.01 + enabled: root.mode === "history" + + ClipboardContent { + id: historyContent + anchors.fill: parent + modal: root + clearConfirmDialog: root.clearConfirmDialog + } + } + + ClipboardEditor { + id: editorView + anchors.fill: parent + opacity: 0 + scale: 0.98 + visible: opacity > 0.01 + enabled: root.mode === "editor" + focus: root.mode === "editor" + modal: root + keyController: keyboardController + } + + states: [ + State { + name: "history" + when: root.mode === "history" + PropertyChanges { + target: historyView + opacity: 1 + scale: 1 + } + PropertyChanges { + target: editorView + opacity: 0 + scale: 0.98 + } + }, + State { + name: "editor" + when: root.mode === "editor" + PropertyChanges { + target: historyView + opacity: 0 + scale: 0.98 + } + PropertyChanges { + target: editorView + opacity: 1 + scale: 1 + } + } + ] + + transitions: [ + Transition { + from: "history" + to: "editor" + ParallelAnimation { + NumberAnimation { + property: "opacity" + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + NumberAnimation { + property: "scale" + duration: Theme.shortDuration + easing.type: Theme.emphasizedEasing + } + } + }, + Transition { + from: "editor" + to: "history" + ParallelAnimation { + NumberAnimation { + property: "opacity" + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + NumberAnimation { + property: "scale" + duration: Theme.shortDuration + easing.type: Theme.emphasizedEasing + } + } + } + ] +} diff --git a/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml b/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml index 176e5925..d40c3134 100644 --- a/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml +++ b/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml @@ -17,74 +17,28 @@ DankModal { active: clipboardHistoryModal.useHyprlandFocusGrab && clipboardHistoryModal.shouldHaveFocus } - property string activeTab: "recents" - onActiveTabChanged: { - ClipboardService.selectedIndex = 0; - ClipboardService.keyboardNavigationActive = false; - } - property bool showKeyboardHints: false - property Component clipboardContent - property int activeImageLoads: 0 - readonly property int maxConcurrentLoads: 3 - - readonly property bool clipboardAvailable: ClipboardService.clipboardAvailable - readonly property bool wtypeAvailable: ClipboardService.wtypeAvailable - readonly property int totalCount: ClipboardService.totalCount - readonly property var clipboardEntries: ClipboardService.clipboardEntries - readonly property var pinnedEntries: ClipboardService.pinnedEntries - readonly property int pinnedCount: ClipboardService.pinnedCount - readonly property var unpinnedEntries: ClipboardService.unpinnedEntries - readonly property int selectedIndex: ClipboardService.selectedIndex - readonly property bool keyboardNavigationActive: ClipboardService.keyboardNavigationActive - property string searchText: ClipboardService.searchText - onSearchTextChanged: ClipboardService.searchText = searchText - - Ref { - service: ClipboardService - } - - property string mode: "history" - onModeChanged: { - if (mode !== "history") { - return; - } - Qt.callLater(function () { - if (contentLoader.item?.searchField) { - contentLoader.item.searchField.forceActiveFocus(); - } - }); - } - - function updateFilteredModel() { - ClipboardService.updateFilteredModel(); - } - - function pasteSelected() { - ClipboardService.pasteSelected(instantClose); - } - function toggle() { if (shouldBeVisible) { hide(); - } else { - show(); + return; } + show(); } function show() { open(); - mode = "history"; - activeImageLoads = 0; shouldHaveFocus = true; - ClipboardService.reset(); - keyboardController.reset(); Qt.callLater(function () { - if (clipboardAvailable) { + if (contentLoader.item) { + contentLoader.item.resetState(); + } + if (clipboardHistoryModal.clipboardAvailable) { if (Theme.isConnectedEffect) { Qt.callLater(() => { - if (clipboardHistoryModal.shouldBeVisible) + if (clipboardHistoryModal.shouldBeVisible) { ClipboardService.refresh(); + } }); } else { ClipboardService.refresh(); @@ -102,62 +56,13 @@ DankModal { } onDialogClosed: { - activeImageLoads = 0; - ClipboardService.reset(); - keyboardController.reset(); - } - - function refreshClipboard() { - ClipboardService.refresh(); - } - - function copyEntry(entry) { - ClipboardService.copyEntry(entry, hide); - } - - function deleteEntry(entry) { - ClipboardService.deleteEntry(entry); - } - - function deletePinnedEntry(entry) { - ClipboardService.deletePinnedEntry(entry, clearConfirmDialog); - } - - function pinEntry(entry) { - ClipboardService.pinEntry(entry); - } - - function unpinEntry(entry) { - ClipboardService.unpinEntry(entry); - } - - function clearAll() { - ClipboardService.clearAll(); - } - - function getEntryPreview(entry) { - return ClipboardService.getEntryPreview(entry); - } - - function getEntryType(entry) { - return ClipboardService.getEntryType(entry); - } - - function editEntry(entry) { - if (!entry) { - return; + if (contentLoader.item) { + contentLoader.item.resetState(); } - if (entry.isImage) { - return; - } - const editor = contentLoader.item?.editorView; - if (!editor) { - return; - } - editor.setEntry(entry); - mode = "editor"; } + readonly property bool clipboardAvailable: ClipboardService.clipboardAvailable + visible: false modalWidth: ClipboardConstants.modalWidth modalHeight: ClipboardConstants.modalHeight @@ -166,16 +71,11 @@ DankModal { borderColor: Theme.outlineMedium borderWidth: 1 enableShadow: true - closeOnEscapeKey: mode !== "editor" + closeOnEscapeKey: (contentLoader.item?.mode ?? "history") !== "editor" onBackgroundClicked: hide() - modalFocusScope.Keys.onPressed: function (event) { - keyboardController.handleKey(event); - } - content: clipboardContent - ClipboardKeyboardController { - id: keyboardController - modal: clipboardHistoryModal + Ref { + service: ClipboardService } ConfirmModal { @@ -200,112 +100,11 @@ DankModal { } } - property var confirmDialog: clearConfirmDialog - - clipboardContent: Component { - Item { - id: viewContainer - - property alias editorView: editorView - property alias searchField: historyContent.searchField - - anchors.fill: parent - - Item { - id: historyView - - anchors.fill: parent - opacity: 1 - scale: 1 - visible: opacity > 0.01 - enabled: clipboardHistoryModal.mode === "history" - - ClipboardContent { - id: historyContent - anchors.fill: parent - modal: clipboardHistoryModal - clearConfirmDialog: clipboardHistoryModal.confirmDialog - } - } - - ClipboardEditor { - id: editorView - - anchors.fill: parent - opacity: 0 - scale: 0.98 - visible: opacity > 0.01 - enabled: clipboardHistoryModal.mode === "editor" - focus: clipboardHistoryModal.mode === "editor" - modal: clipboardHistoryModal - keyController: keyboardController - } - - states: [ - State { - name: "history" - when: clipboardHistoryModal.mode === "history" - PropertyChanges { - target: historyView - opacity: 1 - scale: 1 - } - PropertyChanges { - target: editorView - opacity: 0 - scale: 0.98 - } - }, - State { - name: "editor" - when: clipboardHistoryModal.mode === "editor" - PropertyChanges { - target: historyView - opacity: 0 - scale: 0.98 - } - PropertyChanges { - target: editorView - opacity: 1 - scale: 1 - } - } - ] - - transitions: [ - Transition { - from: "history" - to: "editor" - ParallelAnimation { - NumberAnimation { - property: "opacity" - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - NumberAnimation { - property: "scale" - duration: Theme.shortDuration - easing.type: Theme.emphasizedEasing - } - } - }, - Transition { - from: "editor" - to: "history" - ParallelAnimation { - NumberAnimation { - property: "opacity" - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - NumberAnimation { - property: "scale" - duration: Theme.shortDuration - easing.type: Theme.emphasizedEasing - } - } - } - ] + content: Component { + ClipboardHistoryContent { + clearConfirmDialog: clearConfirmDialog + onCloseRequested: clipboardHistoryModal.hide() + onInstantCloseRequested: clipboardHistoryModal.instantClose() } } } diff --git a/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml b/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml index 924a3d18..27b37019 100644 --- a/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml +++ b/quickshell/Modals/Clipboard/ClipboardHistoryPopout.qml @@ -15,47 +15,20 @@ DankPopout { property var parentWidget: null property var triggerScreen: null property string activeTab: "recents" - property bool showKeyboardHints: false - property int activeImageLoads: 0 - readonly property int maxConcurrentLoads: 3 readonly property bool clipboardAvailable: ClipboardService.clipboardAvailable - readonly property bool wtypeAvailable: ClipboardService.wtypeAvailable - readonly property int totalCount: ClipboardService.totalCount - readonly property var clipboardEntries: ClipboardService.clipboardEntries - readonly property var pinnedEntries: ClipboardService.pinnedEntries readonly property int pinnedCount: ClipboardService.pinnedCount - readonly property var unpinnedEntries: ClipboardService.unpinnedEntries - readonly property int selectedIndex: ClipboardService.selectedIndex - readonly property bool keyboardNavigationActive: ClipboardService.keyboardNavigationActive - property string searchText: ClipboardService.searchText - onSearchTextChanged: ClipboardService.searchText = searchText - + readonly property var confirmDialog: clearConfirmDialog readonly property var modalFocusScope: contentLoader.item ?? null - Ref { - service: ClipboardService - } - - function updateFilteredModel() { - ClipboardService.updateFilteredModel(); - } - - function pasteSelected() { - ClipboardService.pasteSelected(instantClose); - } - - function instantClose() { - close(); - } - function show() { open(); - activeImageLoads = 0; - ClipboardService.reset(); - keyboardController.reset(); Qt.callLater(function () { + if (contentLoader.item) { + contentLoader.item.activeTab = activeTab; + contentLoader.item.resetState(); + } if (contentLoader.item?.searchField) { contentLoader.item.searchField.text = ""; contentLoader.item.searchField.forceActiveFocus(); @@ -65,47 +38,12 @@ DankPopout { function hide() { close(); - activeImageLoads = 0; - ClipboardService.reset(); - keyboardController.reset(); - } - - function refreshClipboard() { - ClipboardService.refresh(); - } - - function copyEntry(entry) { - ClipboardService.copyEntry(entry, hide); - } - - function deleteEntry(entry) { - ClipboardService.deleteEntry(entry); - } - - function deletePinnedEntry(entry) { - ClipboardService.deletePinnedEntry(entry, clearConfirmDialog); - } - - function pinEntry(entry) { - ClipboardService.pinEntry(entry); - } - - function unpinEntry(entry) { - ClipboardService.unpinEntry(entry); } function clearAll() { ClipboardService.clearAll(); } - function getEntryPreview(entry) { - return ClipboardService.getEntryPreview(entry); - } - - function getEntryType(entry) { - return ClipboardService.getEntryType(entry); - } - popupWidth: ClipboardConstants.popoutWidth popupHeight: ClipboardConstants.popoutHeight triggerWidth: 55 @@ -117,20 +55,25 @@ DankPopout { onBackgroundClicked: hide() onShouldBeVisibleChanged: { - if (!shouldBeVisible) + if (!shouldBeVisible) { return; + } if (clipboardAvailable) { if (Theme.isConnectedEffect) { Qt.callLater(() => { - if (root.shouldBeVisible) + if (root.shouldBeVisible) { ClipboardService.refresh(); + } }); } else { ClipboardService.refresh(); } } - keyboardController.reset(); Qt.callLater(function () { + if (contentLoader.item) { + contentLoader.item.activeTab = activeTab; + contentLoader.item.resetState(); + } if (contentLoader.item?.searchField) { contentLoader.item.searchField.text = ""; contentLoader.item.searchField.forceActiveFocus(); @@ -139,14 +82,13 @@ DankPopout { } onPopoutClosed: { - activeImageLoads = 0; - ClipboardService.reset(); - keyboardController.reset(); + if (contentLoader.item) { + contentLoader.item.resetState(); + } } - ClipboardKeyboardController { - id: keyboardController - modal: root + Ref { + service: ClipboardService } ConfirmModal { @@ -155,48 +97,20 @@ DankPopout { confirmButtonColor: Theme.primary } - property var confirmDialog: clearConfirmDialog - content: Component { - FocusScope { - id: contentFocusScope - + ClipboardHistoryContent { LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.childrenInherit: true - focus: true - - property alias searchField: clipboardContentItem.searchField - - Keys.onPressed: function (event) { - keyboardController.handleKey(event); - } + clearConfirmDialog: clearConfirmDialog + onCloseRequested: root.hide() + onInstantCloseRequested: root.close() Component.onCompleted: { - if (root.shouldBeVisible) + activeTab = root.activeTab; + if (root.shouldBeVisible) { forceActiveFocus(); - } - - Connections { - target: root - function onShouldBeVisibleChanged() { - if (root.shouldBeVisible) { - Qt.callLater(() => contentFocusScope.forceActiveFocus()); - } } - function onOpened() { - Qt.callLater(() => { - if (clipboardContentItem.searchField) { - clipboardContentItem.searchField.forceActiveFocus(); - } - }); - } - } - - ClipboardContent { - id: clipboardContentItem - modal: root - clearConfirmDialog: root.confirmDialog } } }