Scrollable OCR panel and copy-text shortcut
The OCR panel overflowed its container on long recognized text; it now clips, caps at 40% of the preview height, and scrolls. Ctrl+Shift+C copies an entry's text content (OCR text or QR payload for images) as plain text, and the QR/OCR panels get Copy chips.
This commit is contained in:
@@ -268,6 +268,22 @@ Item {
|
|||||||
Quickshell.execDetached([root.pluginDir + "/open-entry.sh", result.row.entry.id])
|
Quickshell.execDetached([root.pluginDir + "/open-entry.sh", result.row.entry.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Text content of an entry as a string: the text itself, or for images the
|
||||||
|
// recognized (OCR) text, falling back to a decoded QR payload.
|
||||||
|
function textContent(result) {
|
||||||
|
if (!result) return ""
|
||||||
|
var e = result.row.entry
|
||||||
|
if (e.type === "image") return String(e.ocr || e.qr || "")
|
||||||
|
return String(e.text || "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a plain string (not an entry) to the clipboard and close.
|
||||||
|
function copyText(text) {
|
||||||
|
if (!text) return
|
||||||
|
root.close()
|
||||||
|
Quickshell.execDetached(["wl-copy", "--type", "text/plain", "--", text])
|
||||||
|
}
|
||||||
|
|
||||||
function removeIndex(index) {
|
function removeIndex(index) {
|
||||||
if (index < 0 || index >= root.results.length) return
|
if (index < 0 || index >= root.results.length) return
|
||||||
var entry = root.results[index].row.entry
|
var entry = root.results[index].row.entry
|
||||||
@@ -560,6 +576,9 @@ Item {
|
|||||||
} else if (event.key === Qt.Key_O && (event.modifiers & Qt.ControlModifier)) {
|
} else if (event.key === Qt.Key_O && (event.modifiers & Qt.ControlModifier)) {
|
||||||
root.openResult(root.currentResult)
|
root.openResult(root.currentResult)
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_C && (event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier)) {
|
||||||
|
root.copyText(root.textContent(root.currentResult))
|
||||||
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||||
if (event.modifiers & Qt.ShiftModifier) root.copyResult(root.currentResult)
|
if (event.modifiers & Qt.ShiftModifier) root.copyResult(root.currentResult)
|
||||||
else if (event.modifiers & Qt.AltModifier) root.openResult(root.currentResult)
|
else if (event.modifiers & Qt.AltModifier) root.openResult(root.currentResult)
|
||||||
@@ -927,6 +946,7 @@ Item {
|
|||||||
anchors.leftMargin: root.listWidth + Style.space(14)
|
anchors.leftMargin: root.listWidth + Style.space(14)
|
||||||
result: root.currentResult
|
result: root.currentResult
|
||||||
openAction: function() { root.openResult(root.currentResult) }
|
openAction: function() { root.openResult(root.currentResult) }
|
||||||
|
copyTextAction: function(text) { root.copyText(text) }
|
||||||
visible: root.currentResult !== null
|
visible: root.currentResult !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,6 +992,7 @@ Item {
|
|||||||
{ keys: "enter", hint: "paste" },
|
{ keys: "enter", hint: "paste" },
|
||||||
{ keys: "shift+enter", hint: "copy" },
|
{ keys: "shift+enter", hint: "copy" },
|
||||||
{ keys: "ctrl+o", hint: "open" },
|
{ keys: "ctrl+o", hint: "open" },
|
||||||
|
{ keys: "ctrl+shift+c", hint: "copy text" },
|
||||||
{ keys: "tab", hint: "pin" },
|
{ keys: "tab", hint: "pin" },
|
||||||
{ keys: "ctrl+=", hint: "pause" },
|
{ keys: "ctrl+=", hint: "pause" },
|
||||||
{ keys: "del", hint: "remove" },
|
{ keys: "del", hint: "remove" },
|
||||||
|
|||||||
+80
-8
@@ -14,6 +14,8 @@ Item {
|
|||||||
property string derived: result ? result.row.type : ""
|
property string derived: result ? result.row.type : ""
|
||||||
// Wired by the picker: opens the current result (browser for links).
|
// Wired by the picker: opens the current result (browser for links).
|
||||||
property var openAction: function() {}
|
property var openAction: function() {}
|
||||||
|
// Wired by the picker: copies a string (OCR text, QR payload) to the clipboard.
|
||||||
|
property var copyTextAction: function(text) {}
|
||||||
|
|
||||||
readonly property string font_: Style.font.menuFamily
|
readonly property string font_: Style.font.menuFamily
|
||||||
readonly property color fg: Color.menu.text
|
readonly property color fg: Color.menu.text
|
||||||
@@ -393,6 +395,30 @@ Item {
|
|||||||
font.bold: true
|
font.bold: true
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
radius: height / 2
|
||||||
|
color: Util.alpha(Color.accent, 0.15)
|
||||||
|
width: qrCopyLabel.implicitWidth + Style.space(16)
|
||||||
|
height: Style.space(20)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: qrCopyLabel
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: " Copy"
|
||||||
|
color: Color.accent
|
||||||
|
font.family: root.font_
|
||||||
|
font.pixelSize: Style.font.caption
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.copyTextAction(root.entry ? String(root.entry.qr || "") : "")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
@@ -455,9 +481,13 @@ Item {
|
|||||||
id: ocrPanel
|
id: ocrPanel
|
||||||
visible: !!(root.entry && root.entry.ocr)
|
visible: !!(root.entry && root.entry.ocr)
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: Math.min(Style.space(150), ocrContent.height + Style.space(12))
|
// Never taller than the cap: the body scrolls instead of overflowing
|
||||||
|
// the panel (which used to spill over the image below it).
|
||||||
|
readonly property int maxHeight: Math.max(Style.space(80), Math.floor(parent.height * 0.4))
|
||||||
|
height: Math.min(maxHeight, ocrHeader.height + Style.space(4) + ocrText.implicitHeight + Style.space(12))
|
||||||
radius: Style.cornerRadius
|
radius: Style.cornerRadius
|
||||||
color: root.chipBg
|
color: root.chipBg
|
||||||
|
clip: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: ocrContent
|
id: ocrContent
|
||||||
@@ -469,20 +499,62 @@ Item {
|
|||||||
anchors.rightMargin: Style.space(6)
|
anchors.rightMargin: Style.space(6)
|
||||||
spacing: Style.space(4)
|
spacing: Style.space(4)
|
||||||
|
|
||||||
Text {
|
Row {
|
||||||
text: " Recognized text (OCR)"
|
id: ocrHeader
|
||||||
color: Color.accent
|
width: parent.width
|
||||||
font.family: root.font_
|
spacing: Style.space(8)
|
||||||
font.pixelSize: Style.font.caption
|
|
||||||
font.bold: true
|
Text {
|
||||||
|
text: " Recognized text (OCR)"
|
||||||
|
color: Color.accent
|
||||||
|
font.family: root.font_
|
||||||
|
font.pixelSize: Style.font.caption
|
||||||
|
font.bold: true
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
radius: height / 2
|
||||||
|
color: Util.alpha(Color.accent, 0.15)
|
||||||
|
width: ocrCopyLabel.implicitWidth + Style.space(16)
|
||||||
|
height: Style.space(20)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: ocrCopyLabel
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: " Copy text"
|
||||||
|
color: Color.accent
|
||||||
|
font.family: root.font_
|
||||||
|
font.pixelSize: Style.font.caption
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.copyTextAction(root.entry ? String(root.entry.ocr || "") : "")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
|
id: ocrFlick
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: ocrText.implicitHeight
|
height: ocrPanel.height - ocrHeader.height - Style.space(4) - Style.space(12)
|
||||||
clip: true
|
clip: true
|
||||||
contentWidth: width
|
contentWidth: width
|
||||||
contentHeight: ocrText.implicitHeight
|
contentHeight: ocrText.implicitHeight
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onWheel: function(ev) {
|
||||||
|
if (ev.angleDelta.y < 0) ocrFlick.flick(0, -240)
|
||||||
|
else ocrFlick.flick(0, 240)
|
||||||
|
ev.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: ocrText
|
id: ocrText
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ ever run when you invoke it explicitly.
|
|||||||
| `Enter` | copy to clipboard and paste into the focused window |
|
| `Enter` | copy to clipboard and paste into the focused window |
|
||||||
| `Shift+Enter` | copy only |
|
| `Shift+Enter` | copy only |
|
||||||
| `Ctrl+O` | open a pure link in the browser, an image in the editor, or a file externally. Text entries stay in the picker; use `Return` to paste them. QR images open their **decoded link** in the browser; the preview pane also has clickable "Open link" chips for links and QR payloads |
|
| `Ctrl+O` | open a pure link in the browser, an image in the editor, or a file externally. Text entries stay in the picker; use `Return` to paste them. QR images open their **decoded link** in the browser; the preview pane also has clickable "Open link" chips for links and QR payloads |
|
||||||
|
| `Ctrl+Shift+C` | copy the entry's text content as plain text: for images, the OCR text (or QR payload). The QR and OCR panels also have **Copy** chips |
|
||||||
| `Tab` | pin/unpin |
|
| `Tab` | pin/unpin |
|
||||||
| `Ctrl+=` | pause/resume recording |
|
| `Ctrl+=` | pause/resume recording |
|
||||||
| `Delete` | remove entry · `Shift+Delete` clear all (with confirm) |
|
| `Delete` | remove entry · `Shift+Delete` clear all (with confirm) |
|
||||||
|
|||||||
Reference in New Issue
Block a user