mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-07 19:59:14 -04:00
feat(Clipboard): Clipboard Editor PR Revived (#2492)
* feat(clipboard): Add editing capability to clipboard entries * Add split save menu for clipboard editor * Add clipboard editor shortcuts and hints * Show full clipboard text in editor * feat(Clipboard): Revive ClipboardEditor PR - Original PR #1916 by @nabaco * fix(clipboard): restore Save button targets in editor --------- Co-authored-by: Nachum Barcohen <38861757+nabaco@users.noreply.github.com>
This commit is contained in:
@@ -145,6 +145,7 @@ Item {
|
||||
onDeleteRequested: clipboardContent.modal.deleteEntry(modelData)
|
||||
onPinRequested: clipboardContent.modal.pinEntry(modelData)
|
||||
onUnpinRequested: clipboardContent.modal.unpinEntry(modelData)
|
||||
onEditRequested: clipboardContent.modal.editEntry(modelData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +205,7 @@ Item {
|
||||
onDeleteRequested: clipboardContent.modal.deletePinnedEntry(modelData)
|
||||
onPinRequested: clipboardContent.modal.pinEntry(modelData)
|
||||
onUnpinRequested: clipboardContent.modal.unpinEntry(modelData)
|
||||
onEditRequested: clipboardContent.modal.editEntry(modelData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,519 @@
|
||||
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 positionSaveMenu() {
|
||||
saveMenu.width = Math.max(saveMenuColumn.implicitWidth + saveMenu.padding * 2, saveButton.width);
|
||||
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;
|
||||
}
|
||||
|
||||
function toggleSaveMenu() {
|
||||
if (saveMenu.visible) {
|
||||
saveMenu.close();
|
||||
return;
|
||||
}
|
||||
saveMenu.open();
|
||||
positionSaveMenu();
|
||||
Qt.callLater(positionSaveMenu);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
readonly property int buttonHeight: cancelButton.buttonHeight
|
||||
readonly property int arrowWidth: Theme.iconSizeLarge
|
||||
|
||||
width: cancelButton.width
|
||||
height: buttonHeight
|
||||
|
||||
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 - cancelButton.horizontalPadding
|
||||
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 {
|
||||
z: 1
|
||||
anchors.fill: saveMainArea
|
||||
stateColor: Theme.onPrimary
|
||||
onClicked: root.saveEntry("history")
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
z: 1
|
||||
anchors.fill: saveArrowArea
|
||||
stateColor: Theme.onPrimary
|
||||
onClicked: root.toggleSaveMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: saveMenu
|
||||
parent: Overlay.overlay
|
||||
padding: Theme.spacingM
|
||||
modal: true
|
||||
dim: 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ Rectangle {
|
||||
signal deleteRequested
|
||||
signal pinRequested
|
||||
signal unpinRequested
|
||||
signal editRequested
|
||||
|
||||
readonly property string entryType: modal ? modal.getEntryType(entry) : "text"
|
||||
readonly property string entryPreview: modal ? modal.getEntryPreview(entry) : ""
|
||||
@@ -70,6 +71,20 @@ Rectangle {
|
||||
onClicked: entry.pinned ? unpinRequested() : pinRequested()
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
iconName: "edit"
|
||||
iconSize: Theme.iconSize - 6
|
||||
iconColor: Theme.surfaceText
|
||||
|
||||
onClicked: {
|
||||
if (entryType === "image") {
|
||||
// TODO - forward to editing software
|
||||
} else {
|
||||
editRequested();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 6
|
||||
@@ -142,8 +157,11 @@ Rectangle {
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 80
|
||||
anchors.left: parent.left
|
||||
anchors.right: actionButtons.left
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: mouse => {
|
||||
|
||||
@@ -43,6 +43,18 @@ DankModal {
|
||||
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();
|
||||
}
|
||||
@@ -61,6 +73,7 @@ DankModal {
|
||||
|
||||
function show() {
|
||||
open();
|
||||
mode = "history";
|
||||
activeImageLoads = 0;
|
||||
shouldHaveFocus = true;
|
||||
ClipboardService.reset();
|
||||
@@ -130,6 +143,21 @@ DankModal {
|
||||
return ClipboardService.getEntryType(entry);
|
||||
}
|
||||
|
||||
function editEntry(entry) {
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.isImage) {
|
||||
return;
|
||||
}
|
||||
const editor = contentLoader.item?.editorView;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
editor.setEntry(entry);
|
||||
mode = "editor";
|
||||
}
|
||||
|
||||
visible: false
|
||||
modalWidth: ClipboardConstants.modalWidth
|
||||
modalHeight: ClipboardConstants.modalHeight
|
||||
@@ -138,6 +166,7 @@ DankModal {
|
||||
borderColor: Theme.outlineMedium
|
||||
borderWidth: 1
|
||||
enableShadow: true
|
||||
closeOnEscapeKey: mode !== "editor"
|
||||
onBackgroundClicked: hide()
|
||||
modalFocusScope.Keys.onPressed: function (event) {
|
||||
keyboardController.handleKey(event);
|
||||
@@ -174,9 +203,109 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,24 @@ 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 (ClipboardService.keyboardNavigationActive) {
|
||||
@@ -152,6 +169,10 @@ QtObject {
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
case Qt.Key_E:
|
||||
editSelected();
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Rectangle {
|
||||
readonly property string hintsText: {
|
||||
if (!wtypeAvailable)
|
||||
return I18n.tr("Ctrl+Tab: Switch Tab • Ctrl+S: Pin/Unpin • Shift+Del: Clear All • Esc: Close");
|
||||
return enterToPaste ? I18n.tr("Ctrl+Tab: Switch Tab • Ctrl+S: Pin/Unpin • Shift+Enter: Copy • Shift+Del: Clear All • Esc: Close", "Keyboard hints when enter-to-paste is enabled") : I18n.tr("Ctrl+Tab: Switch Tab • Ctrl+S: Pin/Unpin • Shift+Enter: Paste • Shift+Del: Clear All • Esc: Close");
|
||||
return enterToPaste ? I18n.tr("Ctrl+Tab: Switch Tabs • Ctrl+S: Pin/Unpin • Shift+Enter: Copy • Shift+Del: Clear All • F10: Help • Esc: Close", "Keyboard hints when enter-to-paste is enabled") : I18n.tr("Ctrl+Tab: Switch Tabs • Ctrl+S: Pin/Unpin • Shift+Enter: Paste • Shift+Del: Clear All • F10: Help • Esc: Close");
|
||||
}
|
||||
|
||||
height: ClipboardConstants.keyboardHintsHeight
|
||||
@@ -22,13 +22,17 @@ Rectangle {
|
||||
z: 100
|
||||
|
||||
Column {
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
anchors.centerIn: parent
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
text: keyboardHints.enterToPaste ? I18n.tr("↑/↓: Navigate • Enter: Paste • Del: Delete • F10: Help", "Keyboard hints when enter-to-paste is enabled") : I18n.tr("↑/↓: Navigate • Enter/Ctrl+C: Copy • Del: Delete • F10: Help")
|
||||
text: keyboardHints.enterToPaste ? I18n.tr("↑/↓: Navigate • Enter: Paste • Ctrl+C: Copy • Del: Delete • Ctrl+E: Edit • Ctrl+S: Pin/Unpin • F10: Help", "Keyboard hints when enter-to-paste is enabled") : I18n.tr("↑/↓: Navigate • Enter/Ctrl+C: Copy • Del: Delete • Ctrl+E: Edit • Ctrl+S: Pin/Unpin • F10: Help")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
@@ -36,6 +40,9 @@ Rectangle {
|
||||
text: keyboardHints.hintsText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,17 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
function pasteClipboard(closeCallback) {
|
||||
if (!wtypeAvailable) {
|
||||
ToastService.showError(I18n.tr("wtype not available - install wtype for paste support"));
|
||||
return;
|
||||
}
|
||||
if (closeCallback) {
|
||||
closeCallback();
|
||||
}
|
||||
pasteTimer.start();
|
||||
}
|
||||
|
||||
function pasteEntry(entry, closeCallback) {
|
||||
if (!wtypeAvailable) {
|
||||
ToastService.showError(I18n.tr("wtype not available - install wtype for paste support"));
|
||||
|
||||
Reference in New Issue
Block a user