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:
2026-09-07 16:50:11 +01:00
parent 8127ae4bba
commit b1d98971c9
3 changed files with 102 additions and 8 deletions
+21
View File
@@ -268,6 +268,22 @@ Item {
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) {
if (index < 0 || index >= root.results.length) return
var entry = root.results[index].row.entry
@@ -560,6 +576,9 @@ Item {
} else if (event.key === Qt.Key_O && (event.modifiers & Qt.ControlModifier)) {
root.openResult(root.currentResult)
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) {
if (event.modifiers & Qt.ShiftModifier) root.copyResult(root.currentResult)
else if (event.modifiers & Qt.AltModifier) root.openResult(root.currentResult)
@@ -927,6 +946,7 @@ Item {
anchors.leftMargin: root.listWidth + Style.space(14)
result: root.currentResult
openAction: function() { root.openResult(root.currentResult) }
copyTextAction: function(text) { root.copyText(text) }
visible: root.currentResult !== null
}
@@ -972,6 +992,7 @@ Item {
{ keys: "enter", hint: "paste" },
{ keys: "shift+enter", hint: "copy" },
{ keys: "ctrl+o", hint: "open" },
{ keys: "ctrl+shift+c", hint: "copy text" },
{ keys: "tab", hint: "pin" },
{ keys: "ctrl+=", hint: "pause" },
{ keys: "del", hint: "remove" },
+74 -2
View File
@@ -14,6 +14,8 @@ Item {
property string derived: result ? result.row.type : ""
// Wired by the picker: opens the current result (browser for links).
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 color fg: Color.menu.text
@@ -393,6 +395,30 @@ Item {
font.bold: true
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 {
@@ -455,9 +481,13 @@ Item {
id: ocrPanel
visible: !!(root.entry && root.entry.ocr)
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
color: root.chipBg
clip: true
Column {
id: ocrContent
@@ -469,20 +499,62 @@ Item {
anchors.rightMargin: Style.space(6)
spacing: Style.space(4)
Row {
id: ocrHeader
width: parent.width
spacing: Style.space(8)
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 {
id: ocrFlick
width: parent.width
height: ocrText.implicitHeight
height: ocrPanel.height - ocrHeader.height - Style.space(4) - Style.space(12)
clip: true
contentWidth: width
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 {
id: ocrText
+1
View File
@@ -109,6 +109,7 @@ ever run when you invoke it explicitly.
| `Enter` | copy to clipboard and paste into the focused window |
| `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+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 |
| `Ctrl+=` | pause/resume recording |
| `Delete` | remove entry · `Shift+Delete` clear all (with confirm) |