diff --git a/quickshell/Modals/Clipboard/ClipboardEditor.qml b/quickshell/Modals/Clipboard/ClipboardEditor.qml new file mode 100644 index 00000000..e39c750d --- /dev/null +++ b/quickshell/Modals/Clipboard/ClipboardEditor.qml @@ -0,0 +1,508 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import qs.Common +import qs.Services +import qs.Widgets + +Item { + id: root + + required property var modal + property var keyController: null + + property var entry: null + property string editorText: "" + + function decodeEntryData(data) { + if (!data) { + return ""; + } + if (typeof data !== "string") { + return String(data); + } + + const sanitized = data.replace(/\s+/g, ""); + if (sanitized.length === 0) { + return ""; + } + + try { + const chars = new Array(sanitized.length); + for (let i = 0; i < sanitized.length; i++) { + chars[i] = sanitized.charAt(i); + } + + let buffer = null; + if (typeof Qt !== "undefined" && typeof Qt.atob === "function") { + buffer = Qt.atob(chars); + } else if (typeof atob === "function") { + const binary = atob(sanitized); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + buffer = bytes.buffer; + } + if (!buffer || buffer.byteLength === 0) { + return data; + } + + const bytes = new Uint8Array(buffer); + let binary = ""; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]); + } + + try { + return decodeURIComponent(escape(binary)); + } catch (e) { + return binary; + } + } catch (e) { + return data; + } + } + + function setEntry(newEntry) { + entry = newEntry; + editorText = newEntry?.text ?? newEntry?.preview ?? ""; + if (editField) { + editField.text = editorText; + } + Qt.callLater(function () { + if (editField) { + editField.forceActiveFocus(); + } + }); + + if (!newEntry || newEntry.isImage) { + return; + } + + const requestedId = newEntry.id; + DMSService.sendRequest("clipboard.getEntry", { + "id": requestedId + }, function (response) { + if (response.error) { + return; + } + if (!root.entry || root.entry.id !== requestedId) { + return; + } + const result = response.result; + let fullText = ""; + if (result?.data) { + fullText = root.decodeEntryData(result.data); + } else { + fullText = result?.preview ?? ""; + } + + if (!fullText || fullText.length === 0) { + return; + } + root.editorText = fullText; + if (editField) { + editField.text = fullText; + } + }); + } + + function saveEntry(action) { + const saveAction = action ?? "history"; + DMSService.sendRequest("clipboard.copy", { + "text": root.editorText + }, function (response) { + if (response.error) { + ToastService.showError(I18n.tr("Failed to update clipboard")); + return; + } + if (saveAction === "history") { + modal.mode = "history"; + Qt.callLater(function () { + ClipboardService.reset(); + ClipboardService.refresh(); + if (keyController) { + keyController.reset(); + } + }); + return; + } + if (saveAction === "close") { + modal.hide(); + return; + } + if (saveAction === "paste") { + ClipboardService.pasteClipboard(modal.hide); + } + }); + } + + function toggleSaveMenu() { + if (saveMenu.visible) { + saveMenu.close(); + return; + } + saveMenu.open(); + const pos = saveButton.mapToItem(Overlay.overlay, 0, 0); + const popupW = saveMenu.width; + const popupH = saveMenu.height; + const overlayW = Overlay.overlay.width; + const overlayH = Overlay.overlay.height; + + let x = pos.x + (saveButton.width - popupW) / 2; + let y = pos.y + saveButton.height + 4; + if (y + popupH > overlayH) { + y = pos.y - popupH - 4; + } + + x = Math.max(8, Math.min(x, overlayW - popupW - 8)); + y = Math.max(8, y); + + saveMenu.x = x; + saveMenu.y = y; + } + + Shortcut { + sequences: ["Escape"] + enabled: modal.mode === "editor" + onActivated: modal.mode = "history" + } + + Column { + anchors.fill: parent + anchors.margins: Theme.spacingM + spacing: Theme.spacingM + + Item { + id: editorHeader + width: parent.width + height: ClipboardConstants.headerHeight + + DankActionButton { + iconName: "arrow_back" + iconSize: Theme.iconSize - 4 + iconColor: Theme.surfaceText + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + onClicked: modal.mode = "history" + } + + StyledText { + text: I18n.tr("Edit Clipboard") + font.pixelSize: Theme.fontSizeLarge + color: Theme.surfaceText + font.weight: Font.Medium + anchors.centerIn: parent + } + + DankActionButton { + iconName: "close" + iconSize: Theme.iconSize - 4 + iconColor: Theme.surfaceText + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + onClicked: modal.mode = "history" + } + } + + StyledRect { + id: editFieldContainer + width: parent.width + height: Math.max(Theme.fontSizeMedium * 8, parent.height - editorHeader.height - editorActions.height - Theme.spacingM * 2) + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) + border.color: editField.activeFocus ? Theme.primary : Theme.outlineMedium + border.width: editField.activeFocus ? 2 : 1 + clip: true + + DankIcon { + id: editIcon + name: "edit" + size: Theme.iconSize + color: editField.activeFocus ? Theme.primary : Theme.surfaceVariantText + anchors.left: parent.left + anchors.leftMargin: Theme.spacingM + anchors.top: parent.top + anchors.topMargin: Theme.spacingM + } + + DankFlickable { + id: editScroll + anchors.left: editIcon.right + anchors.leftMargin: Theme.spacingS + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.rightMargin: Theme.spacingM + anchors.topMargin: Theme.spacingS + anchors.bottomMargin: Theme.spacingS + clip: true + contentWidth: width + contentHeight: editField.height + + TextEdit { + id: editField + width: editScroll.width + height: Math.max(editScroll.height, contentHeight) + text: root.editorText + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText + wrapMode: TextEdit.Wrap + selectByMouse: true + onTextChanged: root.editorText = text + Keys.onPressed: function (event) { + const hasCtrl = (event.modifiers & Qt.ControlModifier) !== 0; + const hasShift = (event.modifiers & Qt.ShiftModifier) !== 0; + + if (hasCtrl && event.key === Qt.Key_S) { + root.saveEntry(hasShift ? "close" : "history"); + event.accepted = true; + return; + } + if (hasCtrl && hasShift && event.key === Qt.Key_V) { + root.saveEntry("paste"); + event.accepted = true; + return; + } + } + } + } + + StyledText { + text: I18n.tr("Edit clipboard text") + font.pixelSize: Theme.fontSizeMedium + color: Theme.outlineButton + anchors.left: editScroll.left + anchors.right: editScroll.right + anchors.top: editScroll.top + anchors.bottom: editScroll.bottom + visible: editField.text.length === 0 && !editField.activeFocus + wrapMode: Text.WordWrap + } + } + + Row { + id: editorActions + width: parent.width + spacing: Theme.spacingS + + Item { + id: buttonSpacer + width: Math.max(0, parent.width - cancelButton.width - saveButton.width - Theme.spacingS) + height: 1 + } + + DankButton { + id: cancelButton + text: I18n.tr("Cancel") + backgroundColor: Theme.surfaceContainerHigh + textColor: Theme.surfaceText + onClicked: modal.mode = "history" + } + + Item { + id: saveButton + property int arrowWidth: Theme.iconSize + Theme.spacingS + width: cancelButton.implicitWidth + height: cancelButton.implicitHeight + + Rectangle { + anchors.fill: parent + radius: Theme.cornerRadius + color: Theme.primary + } + + Item { + id: saveMainArea + anchors.left: parent.left + anchors.right: saveArrowArea.left + anchors.top: parent.top + anchors.bottom: parent.bottom + } + + StyledText { + text: I18n.tr("Save") + font.pixelSize: Theme.fontSizeMedium + font.weight: Font.Medium + color: Theme.onPrimary + anchors.centerIn: saveMainArea + } + + Item { + id: saveArrowArea + width: saveButton.arrowWidth + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } + + Rectangle { + width: 1 + height: parent.height - Theme.spacingM + color: Theme.withAlpha(Theme.onPrimary, 0.2) + anchors.right: saveArrowArea.left + anchors.verticalCenter: parent.verticalCenter + } + + DankIcon { + name: saveMenu.visible ? "expand_less" : "expand_more" + size: Theme.iconSizeSmall + color: Theme.onPrimary + anchors.centerIn: saveArrowArea + } + + StateLayer { + anchors.fill: saveMainArea + stateColor: Theme.onPrimary + onClicked: root.saveEntry("history") + } + + StateLayer { + anchors.fill: saveArrowArea + stateColor: Theme.onPrimary + onClicked: root.toggleSaveMenu() + } + } + } + + Popup { + id: saveMenu + parent: Overlay.overlay + width: saveMenuColumn.implicitWidth + padding * 2 + padding: Theme.spacingM + modal: false + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + background: StyledRect { + radius: Theme.cornerRadius + color: Theme.surfaceContainer + border.color: Theme.outlineMedium + border.width: 1 + } + + contentItem: Column { + id: saveMenuColumn + spacing: Theme.spacingXS + + StyledRect { + implicitWidth: saveMenuRow.implicitWidth + Theme.spacingS * 2 + implicitHeight: saveMenuRow.implicitHeight + Theme.spacingS * 2 + radius: Theme.cornerRadius + color: saveMenuSaveArea.containsMouse ? Theme.surfaceVariant : "transparent" + + Row { + id: saveMenuRow + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "save" + size: Theme.iconSizeSmall + color: Theme.surfaceText + } + + StyledText { + text: I18n.tr("Save") + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText + } + } + + MouseArea { + id: saveMenuSaveArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + saveMenu.close(); + root.saveEntry("history"); + } + } + } + + StyledRect { + implicitWidth: saveMenuCloseRow.implicitWidth + Theme.spacingS * 2 + implicitHeight: saveMenuCloseRow.implicitHeight + Theme.spacingS * 2 + radius: Theme.cornerRadius + color: saveMenuCloseArea.containsMouse ? Theme.surfaceVariant : "transparent" + + Row { + id: saveMenuCloseRow + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "close" + size: Theme.iconSizeSmall + color: Theme.surfaceText + } + + StyledText { + text: I18n.tr("Save and close") + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText + } + } + + MouseArea { + id: saveMenuCloseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + saveMenu.close(); + root.saveEntry("close"); + } + } + } + + StyledRect { + implicitWidth: saveMenuPasteRow.implicitWidth + Theme.spacingS * 2 + implicitHeight: saveMenuPasteRow.implicitHeight + Theme.spacingS * 2 + radius: Theme.cornerRadius + color: saveMenuPasteArea.containsMouse ? Theme.surfaceVariant : "transparent" + opacity: modal.wtypeAvailable ? 1 : 0.5 + + Row { + id: saveMenuPasteRow + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "content_paste" + size: Theme.iconSizeSmall + color: Theme.surfaceText + } + + StyledText { + text: I18n.tr("Save and paste") + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText + } + } + + MouseArea { + id: saveMenuPasteArea + anchors.fill: parent + hoverEnabled: true + enabled: modal.wtypeAvailable + cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: { + saveMenu.close(); + root.saveEntry("paste"); + } + } + } + } + } + } +} diff --git a/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml b/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml index b5139e41..176e5925 100644 --- a/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml +++ b/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml @@ -1,13 +1,11 @@ pragma ComponentBehavior: Bound import QtQuick -import QtQuick.Controls import Quickshell.Hyprland import qs.Common import qs.Modals.Clipboard import qs.Modals.Common import qs.Services -import qs.Widgets DankModal { id: clipboardHistoryModal @@ -24,7 +22,6 @@ DankModal { ClipboardService.selectedIndex = 0; ClipboardService.keyboardNavigationActive = false; } - property var editClipboardModal: null property bool showKeyboardHints: false property Component clipboardContent property int activeImageLoads: 0 @@ -172,34 +169,6 @@ DankModal { closeOnEscapeKey: mode !== "editor" onBackgroundClicked: hide() modalFocusScope.Keys.onPressed: function (event) { - if (mode === "history" && (event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab)) { - activeTab = activeTab === "recents" ? "saved" : "recents"; - event.accepted = true; - return; - } - if (mode === "history" && (event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_S) { - const entries = activeTab === "saved" ? pinnedEntries : unpinnedEntries; - if (entries && entries.length > 0) { - const index = ClipboardService.selectedIndex >= 0 && ClipboardService.selectedIndex < entries.length ? ClipboardService.selectedIndex : 0; - const entry = entries[index]; - if (activeTab === "saved") { - unpinEntry(entry); - } else { - pinEntry(entry); - } - } - event.accepted = true; - return; - } - if (mode === "history" && (event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_E) { - const entries = activeTab === "saved" ? pinnedEntries : unpinnedEntries; - if (entries && entries.length > 0) { - const index = ClipboardService.selectedIndex >= 0 && ClipboardService.selectedIndex < entries.length ? ClipboardService.selectedIndex : 0; - editEntry(entries[index]); - } - event.accepted = true; - return; - } keyboardController.handleKey(event); } content: clipboardContent @@ -259,7 +228,7 @@ DankModal { } } - Item { + ClipboardEditor { id: editorView anchors.fill: parent @@ -268,460 +237,8 @@ DankModal { visible: opacity > 0.01 enabled: clipboardHistoryModal.mode === "editor" focus: clipboardHistoryModal.mode === "editor" - - Shortcut { - sequences: ["Escape"] - enabled: clipboardHistoryModal.mode === "editor" - onActivated: clipboardHistoryModal.mode = "history" - } - - property var entry: null - property string editorText: "" - - function setEntry(newEntry) { - entry = newEntry; - editorText = newEntry?.text ?? newEntry?.preview ?? ""; - if (editField) { - editField.text = editorText; - } - Qt.callLater(function () { - if (editField) { - editField.forceActiveFocus(); - } - }); - - if (!newEntry || newEntry.isImage) { - return; - } - - const requestedId = newEntry.id; - DMSService.sendRequest("clipboard.getEntry", { - "id": requestedId - }, function (response) { - if (response.error) { - return; - } - if (!editorView.entry || editorView.entry.id !== requestedId) { - return; - } - const rawText = response.result?.text ?? response.result?.content ?? response.result?.data ?? ""; - let fullText = rawText; - try { - const sanitized = rawText.replace(/\s+/g, ""); - const decoder = (typeof Qt !== "undefined" && typeof Qt.atob === "function") ? Qt.atob : (typeof atob === "function" ? atob : null); - if (decoder) { - const decoded = decoder(sanitized); - fullText = decoded; - try { - fullText = decodeURIComponent(escape(decoded)); - } catch (e) { - fullText = decoded; - } - } - } catch (e) { - fullText = rawText; - } - - if (!fullText || fullText.length === 0) { - return; - } - editorView.editorText = fullText; - if (editField) { - editField.text = fullText; - } - }); - } - - function saveEntry(action) { - const saveAction = action ?? "history"; - DMSService.sendRequest("clipboard.copy", { - "text": editorView.editorText - }, function (response) { - if (response.error) { - ToastService.showError(I18n.tr("Failed to update clipboard")); - return; - } - if (saveAction === "history") { - clipboardHistoryModal.mode = "history"; - Qt.callLater(function () { - ClipboardService.reset(); - ClipboardService.refresh(); - keyboardController.reset(); - }); - return; - } - if (saveAction === "close") { - clipboardHistoryModal.hide(); - return; - } - if (saveAction === "paste") { - ClipboardService.pasteClipboard(clipboardHistoryModal.hide); - } - }); - } - - function toggleSaveMenu() { - if (saveMenu.visible) { - saveMenu.close(); - return; - } - saveMenu.open(); - const pos = saveButton.mapToItem(Overlay.overlay, 0, 0); - const popupW = saveMenu.width; - const popupH = saveMenu.height; - const overlayW = Overlay.overlay.width; - const overlayH = Overlay.overlay.height; - - let x = pos.x + (saveButton.width - popupW) / 2; - let y = pos.y + saveButton.height + 4; - if (y + popupH > overlayH) { - y = pos.y - popupH - 4; - } - - x = Math.max(8, Math.min(x, overlayW - popupW - 8)); - y = Math.max(8, y); - - saveMenu.x = x; - saveMenu.y = y; - } - - Column { - anchors.fill: parent - anchors.margins: Theme.spacingM - spacing: Theme.spacingM - - Item { - id: editorHeader - width: parent.width - height: ClipboardConstants.headerHeight - - DankActionButton { - iconName: "arrow_back" - iconSize: Theme.iconSize - 4 - iconColor: Theme.surfaceText - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - onClicked: clipboardHistoryModal.mode = "history" - } - - StyledText { - text: I18n.tr("Edit Clipboard") - font.pixelSize: Theme.fontSizeLarge - color: Theme.surfaceText - font.weight: Font.Medium - anchors.centerIn: parent - } - - DankActionButton { - iconName: "close" - iconSize: Theme.iconSize - 4 - iconColor: Theme.surfaceText - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - onClicked: clipboardHistoryModal.mode = "history" - } - } - - StyledRect { - id: editFieldContainer - width: parent.width - height: Math.max(Theme.fontSizeMedium * 8, parent.height - editorHeader.height - editorActions.height - Theme.spacingM * 2) - radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) - border.color: editField.activeFocus ? Theme.primary : Theme.outlineMedium - border.width: editField.activeFocus ? 2 : 1 - clip: true - - DankIcon { - id: editIcon - name: "edit" - size: Theme.iconSize - color: editField.activeFocus ? Theme.primary : Theme.surfaceVariantText - anchors.left: parent.left - anchors.leftMargin: Theme.spacingM - anchors.top: parent.top - anchors.topMargin: Theme.spacingM - } - - DankFlickable { - id: editScroll - anchors.left: editIcon.right - anchors.leftMargin: Theme.spacingS - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.rightMargin: Theme.spacingM - anchors.topMargin: Theme.spacingS - anchors.bottomMargin: Theme.spacingS - clip: true - contentWidth: width - contentHeight: editField.height - - TextEdit { - id: editField - width: editScroll.width - height: Math.max(editScroll.height, contentHeight) - text: editorView.editorText - font.pixelSize: Theme.fontSizeMedium - color: Theme.surfaceText - wrapMode: TextEdit.Wrap - selectByMouse: true - onTextChanged: editorView.editorText = text - Keys.onPressed: function (event) { - const hasCtrl = (event.modifiers & Qt.ControlModifier) !== 0; - const hasShift = (event.modifiers & Qt.ShiftModifier) !== 0; - - if (hasCtrl && event.key === Qt.Key_S) { - editorView.saveEntry(hasShift ? "close" : "history"); - event.accepted = true; - return; - } - if (hasCtrl && hasShift && event.key === Qt.Key_V) { - editorView.saveEntry("paste"); - event.accepted = true; - return; - } - } - } - } - - StyledText { - text: I18n.tr("Edit clipboard text") - font.pixelSize: Theme.fontSizeMedium - color: Theme.outlineButton - anchors.left: editScroll.left - anchors.right: editScroll.right - anchors.top: editScroll.top - anchors.bottom: editScroll.bottom - visible: editField.text.length === 0 && !editField.activeFocus - wrapMode: Text.WordWrap - } - } - - Row { - id: editorActions - width: parent.width - spacing: Theme.spacingS - - Item { - id: buttonSpacer - width: Math.max(0, parent.width - cancelButton.width - saveButton.width - Theme.spacingS) - height: 1 - } - - DankButton { - id: cancelButton - text: I18n.tr("Cancel") - backgroundColor: Theme.surfaceContainerHigh - textColor: Theme.surfaceText - onClicked: clipboardHistoryModal.mode = "history" - } - - Item { - id: saveButton - property int arrowWidth: 32 - property int horizontalPadding: Theme.spacingL - width: cancelButton.width - height: 40 - - Rectangle { - anchors.fill: parent - radius: Theme.cornerRadius - color: Theme.primary - } - - Item { - id: saveMainArea - anchors.left: parent.left - anchors.right: saveArrowArea.left - anchors.top: parent.top - anchors.bottom: parent.bottom - } - - StyledText { - id: saveLabel - text: I18n.tr("Save") - font.pixelSize: Theme.fontSizeMedium - font.weight: Font.Medium - color: Theme.onPrimary - anchors.centerIn: saveMainArea - } - - Item { - id: saveArrowArea - width: saveButton.arrowWidth - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - } - - Rectangle { - width: 1 - height: parent.height - Theme.spacingM - color: Theme.withAlpha(Theme.onPrimary, 0.2) - anchors.right: saveArrowArea.left - anchors.verticalCenter: parent.verticalCenter - } - - DankIcon { - name: saveMenu.visible ? "expand_less" : "expand_more" - size: Theme.iconSizeSmall - color: Theme.onPrimary - anchors.centerIn: saveArrowArea - } - - StateLayer { - anchors.fill: saveMainArea - stateColor: Theme.onPrimary - onClicked: editorView.saveEntry("history") - } - - StateLayer { - anchors.fill: saveArrowArea - stateColor: Theme.onPrimary - onClicked: editorView.toggleSaveMenu() - } - } - } - - Popup { - id: saveMenu - parent: Overlay.overlay - width: 220 - padding: Theme.spacingM - modal: false - focus: true - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - background: StyledRect { - radius: Theme.cornerRadius - color: Theme.surfaceContainer - border.color: Theme.outlineMedium - border.width: 1 - } - - contentItem: Column { - id: saveMenuColumn - spacing: Theme.spacingXS - - StyledRect { - width: saveMenu.width - saveMenu.padding * 2 - height: 32 - radius: Theme.cornerRadius - color: saveMenuSaveArea.containsMouse ? Theme.surfaceVariant : "transparent" - - Row { - anchors.fill: parent - anchors.leftMargin: Theme.spacingS - spacing: Theme.spacingS - - DankIcon { - name: "save" - size: Theme.iconSizeSmall - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Save") - font.pixelSize: Theme.fontSizeMedium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - } - - MouseArea { - id: saveMenuSaveArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - saveMenu.close(); - editorView.saveEntry("history"); - } - } - } - - StyledRect { - width: saveMenu.width - saveMenu.padding * 2 - height: 32 - radius: Theme.cornerRadius - color: saveMenuCloseArea.containsMouse ? Theme.surfaceVariant : "transparent" - - Row { - anchors.fill: parent - anchors.leftMargin: Theme.spacingS - spacing: Theme.spacingS - - DankIcon { - name: "close" - size: Theme.iconSizeSmall - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Save and close") - font.pixelSize: Theme.fontSizeMedium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - } - - MouseArea { - id: saveMenuCloseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - saveMenu.close(); - editorView.saveEntry("close"); - } - } - } - - StyledRect { - width: saveMenu.width - saveMenu.padding * 2 - height: 32 - radius: Theme.cornerRadius - color: saveMenuPasteArea.containsMouse ? Theme.surfaceVariant : "transparent" - opacity: clipboardHistoryModal.wtypeAvailable ? 1 : 0.5 - - Row { - anchors.fill: parent - anchors.leftMargin: Theme.spacingS - spacing: Theme.spacingS - - DankIcon { - name: "content_paste" - size: Theme.iconSizeSmall - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Save and paste") - font.pixelSize: Theme.fontSizeMedium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - } - - MouseArea { - id: saveMenuPasteArea - anchors.fill: parent - hoverEnabled: true - enabled: clipboardHistoryModal.wtypeAvailable - cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor - onClicked: { - saveMenu.close(); - editorView.saveEntry("paste"); - } - } - } - } - } - } + modal: clipboardHistoryModal + keyController: keyboardController } states: [ diff --git a/quickshell/Modals/Clipboard/ClipboardKeyboardController.qml b/quickshell/Modals/Clipboard/ClipboardKeyboardController.qml index c7fd0004..1798426b 100644 --- a/quickshell/Modals/Clipboard/ClipboardKeyboardController.qml +++ b/quickshell/Modals/Clipboard/ClipboardKeyboardController.qml @@ -66,12 +66,27 @@ QtObject { } } + function editSelected() { + const entries = modal.activeTab === "saved" ? ClipboardService.pinnedEntries : ClipboardService.unpinnedEntries; + if (!entries || entries.length === 0) { + return; + } + const index = ClipboardService.selectedIndex >= 0 && ClipboardService.selectedIndex < entries.length ? ClipboardService.selectedIndex : 0; + modal.editEntry(entries[index]); + } + function handleKey(event) { + if (modal.mode === "editor") { + if (event.key === Qt.Key_Escape) { + modal.mode = "history"; + event.accepted = true; + } + return; + } + switch (event.key) { case Qt.Key_Escape: - if (modal.mode === "editor") { - modal.mode = "history"; - } else if (ClipboardService.keyboardNavigationActive) { + if (ClipboardService.keyboardNavigationActive) { ClipboardService.keyboardNavigationActive = false; } else { modal.hide(); @@ -154,6 +169,10 @@ QtObject { event.accepted = true; } return; + case Qt.Key_E: + editSelected(); + event.accepted = true; + return; } }