kde/gui: add Acme-style mouse chording to prompt input

Implement proper Plan 9/Acme mouse chording in the input area:
- B1 = select (TextArea default)
- B2 = execute selection as prompt
- B3 = plumb selection or context menu
- B1+B2 = cut (chord while selecting)
- B1+B3 = paste (chord while selecting)

Uses MouseArea overlay that detects when B1 is held while B2/B3
is pressed to trigger chord actions. Context menu items now show
the chord shortcuts (Cut B1+B2, Paste B1+B3).

Placeholder text shows the mouse action reference.
This commit is contained in:
Levi Neely 2026-09-04 14:23:58 +02:00
parent eb12627ea0
commit b25b99be4d
1 changed files with 69 additions and 16 deletions

View File

@ -307,7 +307,7 @@ Item {
ToolTip.text: "Clear input"
}
// TextArea in ScrollView for overflow, with context menu overlay
// TextArea in ScrollView for overflow, with mouse chording
Item {
Layout.fillWidth: true
Layout.maximumHeight: 200
@ -327,7 +327,7 @@ Item {
if (!ollie.activeSessionId) return "No session"
if (!ollie.activeAgentId) return "No agent"
if (ollie.isSessionPaused(ollie.activeSessionId)) return "Session paused"
return "Message... (B2=execute selection, B3=plumb)"
return "B1=sel B2=exec B3=plumb B1+B2=cut B1+B3=paste"
}
enabled: ollie.daemonConnected && ollie.activeSessionId !== "" && ollie.activeAgentId !== "" && !ollie.isSessionPaused(ollie.activeSessionId)
wrapMode: TextEdit.Wrap
@ -350,23 +350,76 @@ Item {
event.accepted = true
}
}
}
}
// Acme-style mouse handling in input
TapHandler {
acceptedButtons: Qt.MiddleButton
onTapped: {
// B2: Execute selection as prompt immediately
var sel = promptInput.selectedText
if (sel && sel.trim() !== "") {
ollie.submit(sel.trim())
}
// Acme-style mouse chording overlay
// B1 = select (handled by TextArea)
// B2 = execute selection
// B3 = plumb selection / context menu
// B1+B2 = cut (snarf)
// B1+B3 = paste
MouseArea {
id: chordArea
anchors.fill: parent
acceptedButtons: Qt.MiddleButton | Qt.RightButton
propagateComposedEvents: true
cursorShape: Qt.IBeamCursor
// Track if B1 (left) is currently held for chord detection
property bool leftHeld: false
// We can't directly detect B1 here since TextArea handles it,
// but we can check mouse.buttons in the handlers
onPressed: function(mouse) {
// Check if this is a chord (B1 already held)
if (mouse.buttons & Qt.LeftButton) {
leftHeld = true
if (mouse.button === Qt.MiddleButton) {
// B1+B2 = cut
promptInput.cut()
mouse.accepted = true
return
} else if (mouse.button === Qt.RightButton) {
// B1+B3 = paste
promptInput.paste()
mouse.accepted = true
return
}
}
leftHeld = false
mouse.accepted = false // Let it propagate for non-chord
}
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: promptContextMenu.popup()
onClicked: function(mouse) {
// Only handle non-chord clicks
if (leftHeld) {
leftHeld = false
return
}
if (mouse.button === Qt.MiddleButton) {
// B2: Execute selection as prompt
var sel = promptInput.selectedText
if (sel && sel.trim() !== "") {
ollie.submit(sel.trim())
} else {
// No selection - paste from clipboard and execute?
// Or just ignore. For now, ignore.
}
} else if (mouse.button === Qt.RightButton) {
// B3: Plumb selection or show context menu
var sel = promptInput.selectedText
if (sel && sel.trim() !== "" && plumber.available()) {
plumber.plumb(sel.trim())
} else {
promptContextMenu.popup()
}
}
}
onReleased: {
leftHeld = false
}
}
@ -390,7 +443,7 @@ Item {
}
MenuSeparator {}
MenuItem {
text: "Cut"
text: "Cut (B1+B2)"
enabled: promptInput.selectedText.length > 0
onTriggered: promptInput.cut()
}
@ -400,7 +453,7 @@ Item {
onTriggered: promptInput.copy()
}
MenuItem {
text: "Paste"
text: "Paste (B1+B3)"
enabled: promptInput.canPaste
onTriggered: promptInput.paste()
}