gui: add text selection and context menus
Chat blocks: - Enable selectByMouse and persistentSelection for text selection - Add context menu with Copy and Select All - Disable ListView drag during text selection (preserves wheel scroll) - I-beam cursor over text content Prompt input: - Enable selectByMouse and persistentSelection - Add context menu with Cut, Copy, Paste, Select All - Remove ScrollView wrapper (TextArea handles overflow natively) - I-beam cursor
This commit is contained in:
parent
624b7ad66d
commit
f086400bf3
105
gui/main.qml
105
gui/main.qml
|
|
@ -1375,16 +1375,78 @@ property bool blockExpanded: {
|
|||
|
||||
// Block content is Markdown rendered by ChatBlockModel.
|
||||
TextEdit {
|
||||
id: blockContent
|
||||
Layout.fillWidth: true
|
||||
visible: blockItem.blockExpanded
|
||||
text: blockRenderedHtml || ""
|
||||
textFormat: Text.RichText
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
persistentSelection: true
|
||||
wrapMode: TextEdit.Wrap
|
||||
font.family: theme.fontFamily || Qt.application.font.family
|
||||
font.pointSize: theme.fontSize > 0 ? theme.fontSize : 12
|
||||
// Note: don't set 'color' here — it can override inline styles from
|
||||
// syntax highlighting in Qt5. The HTML content should set colors.
|
||||
|
||||
// Selection colors
|
||||
selectionColor: theme.highlightColor
|
||||
selectedTextColor: theme.highlightedTextColor
|
||||
|
||||
// Restore ListView scrolling when focus is lost
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus)
|
||||
chatView.interactive = true
|
||||
}
|
||||
|
||||
// Context menu for text operations
|
||||
Menu {
|
||||
id: textContextMenu
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
enabled: blockContent.selectedText.length > 0
|
||||
onTriggered: blockContent.copy()
|
||||
}
|
||||
MenuItem {
|
||||
text: "Select All"
|
||||
onTriggered: blockContent.selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse handling for selection (left button)
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
propagateComposedEvents: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onPressed: function(mouse) {
|
||||
chatView.interactive = false
|
||||
mouse.accepted = false // Let TextEdit handle selection
|
||||
}
|
||||
onReleased: function(mouse) {
|
||||
chatView.interactive = true
|
||||
mouse.accepted = false
|
||||
}
|
||||
onWheel: function(wheel) {
|
||||
// Always allow wheel scrolling, even during selection
|
||||
chatView.contentY = Math.max(0, Math.min(
|
||||
chatView.contentHeight - chatView.height,
|
||||
chatView.contentY - wheel.angleDelta.y
|
||||
))
|
||||
wheel.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
// Context menu (right button) - separate to avoid deselection
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onClicked: function(mouse) {
|
||||
textContextMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1398,16 +1460,15 @@ property bool blockExpanded: {
|
|||
Layout.margins: 6
|
||||
spacing: 4
|
||||
|
||||
ScrollView {
|
||||
id: promptScroll
|
||||
// TextArea without ScrollView - wheel scrolls when content overflows
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumHeight: 200
|
||||
Layout.preferredHeight: Math.min(promptInput.contentHeight + 16, 200)
|
||||
clip: true
|
||||
|
||||
|
||||
TextArea {
|
||||
id: promptInput
|
||||
Layout.fillWidth: true
|
||||
anchors.fill: parent
|
||||
placeholderText: {
|
||||
if (!ollie.daemonConnected) return "Not connected"
|
||||
if (!ollie.activeSessionId) return "No session"
|
||||
|
|
@ -1417,6 +1478,8 @@ property bool blockExpanded: {
|
|||
}
|
||||
enabled: ollie.daemonConnected && ollie.activeSessionId !== "" && ollie.activeAgentId !== "" && !ollie.isSessionPaused(ollie.activeSessionId)
|
||||
wrapMode: TextEdit.Wrap
|
||||
selectByMouse: true
|
||||
persistentSelection: true
|
||||
|
||||
property string _lastText: ""
|
||||
property int _lastCursorPos: 0
|
||||
|
|
@ -1438,6 +1501,38 @@ property bool blockExpanded: {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Context menu overlay - only catches right-click
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: promptContextMenu.popup()
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: promptContextMenu
|
||||
MenuItem {
|
||||
text: "Cut"
|
||||
enabled: promptInput.selectedText.length > 0
|
||||
onTriggered: promptInput.cut()
|
||||
}
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
enabled: promptInput.selectedText.length > 0
|
||||
onTriggered: promptInput.copy()
|
||||
}
|
||||
MenuItem {
|
||||
text: "Paste"
|
||||
enabled: promptInput.canPaste
|
||||
onTriggered: promptInput.paste()
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuItem {
|
||||
text: "Select All"
|
||||
onTriggered: promptInput.selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Reference in New Issue