mirror of https://github.com/rivo/tview.git
Implemented the clipboard.
This commit is contained in:
parent
8b7b755a7f
commit
ad62b2aa7c
106
textarea.go
106
textarea.go
|
@ -115,7 +115,7 @@ type textAreaSpan struct {
|
||||||
// Text can be selected by moving the cursor while holding the Shift key. Thus
|
// Text can be selected by moving the cursor while holding the Shift key. Thus
|
||||||
// when text is selected:
|
// when text is selected:
|
||||||
//
|
//
|
||||||
// - Entering a character (rune) will replace the selected text with the new
|
// - Entering a character will replace the selected text with the new
|
||||||
// character.
|
// character.
|
||||||
// - Backspace, delete, Ctrl-H, Ctrl-D: Delete the selected text.
|
// - Backspace, delete, Ctrl-H, Ctrl-D: Delete the selected text.
|
||||||
// - Ctrl-Q: Copy the selected text into the clipboard, unselect the text.
|
// - Ctrl-Q: Copy the selected text into the clipboard, unselect the text.
|
||||||
|
@ -128,13 +128,14 @@ type textAreaSpan struct {
|
||||||
// because the Ctrl-C key is the default key to stop the application. If your
|
// because the Ctrl-C key is the default key to stop the application. If your
|
||||||
// application frees up the global Ctrl-C key and you want to bind it to the
|
// application frees up the global Ctrl-C key and you want to bind it to the
|
||||||
// "copy to clipboard" function, you may use [Box.SetInputCapture] to override
|
// "copy to clipboard" function, you may use [Box.SetInputCapture] to override
|
||||||
// the Ctrl-Q key to implement copying to the clipboard.
|
// the Ctrl-Q key to implement copying to the clipboard. Note that using your
|
||||||
|
// terminal's / operating system's key bindings for copy+paste functionality may
|
||||||
|
// not have the expected effect as tview will not be able to handle these keys.
|
||||||
//
|
//
|
||||||
// Similarly, if you want to implement your own clipboard (or make use of your
|
// Similarly, if you want to implement your own clipboard (or make use of your
|
||||||
// operating system's clipboard), you can also use [Box.SetInputCapture] to
|
// operating system's clipboard), you can use [Box.SetInputCapture] to override
|
||||||
// override the key binds for copy, cut, and paste. The GetSelection(), ReplaceText(),
|
// the key binds for copy, cut, and paste. [TextArea.SetClipboard] provides all
|
||||||
// and SetSelection() provide all the functionality needed for your own
|
// the functionality needed to implement your own clipboard.
|
||||||
// clipboard. TODO: This will need to be reviewed.
|
|
||||||
//
|
//
|
||||||
// The text area also supports Undo:
|
// The text area also supports Undo:
|
||||||
//
|
//
|
||||||
|
@ -239,6 +240,18 @@ type TextArea struct {
|
||||||
// The textAreaSpan position with state for the actual next character.
|
// The textAreaSpan position with state for the actual next character.
|
||||||
pos [3]int
|
pos [3]int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clipboard related fields:
|
||||||
|
|
||||||
|
// The internal clipboard.
|
||||||
|
clipboard string
|
||||||
|
|
||||||
|
// The function to call when the user copies/cuts a text selection to the
|
||||||
|
// clipboard.
|
||||||
|
copyToClipboard func(string)
|
||||||
|
|
||||||
|
// The function to call when the user pastes text from the clipboard.
|
||||||
|
pasteFromClipboard func() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTextArea returns a new text area. Use [TextArea.SetText] to set the
|
// NewTextArea returns a new text area. Use [TextArea.SetText] to set the
|
||||||
|
@ -258,6 +271,7 @@ func NewTextArea() *TextArea {
|
||||||
t.spans[1] = textAreaSpan{previous: 0, next: -1}
|
t.spans[1] = textAreaSpan{previous: 0, next: -1}
|
||||||
t.cursor.pos = [3]int{1, 0, -1}
|
t.cursor.pos = [3]int{1, 0, -1}
|
||||||
t.selectionStart = t.cursor
|
t.selectionStart = t.cursor
|
||||||
|
t.SetClipboard(nil, nil)
|
||||||
|
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
@ -378,6 +392,31 @@ func (t *TextArea) SetOffset(row, column int) *TextArea {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetClipboard allows you to implement your own clipboard by providing a
|
||||||
|
// function that is called when the user wishes to store text in the clipboard
|
||||||
|
// (copyToClipboard) and a function that is called when the user wishes to
|
||||||
|
// retrieve text from the clipboard (pasteFromClipboard).
|
||||||
|
//
|
||||||
|
// Providing nil values will cause the default clipboard implementation to be
|
||||||
|
// used.
|
||||||
|
func (t *TextArea) SetClipboard(copyToClipboard func(string), pasteFromClipboard func() string) *TextArea {
|
||||||
|
t.copyToClipboard = copyToClipboard
|
||||||
|
if t.copyToClipboard == nil {
|
||||||
|
t.copyToClipboard = func(text string) {
|
||||||
|
t.clipboard = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.pasteFromClipboard = pasteFromClipboard
|
||||||
|
if t.pasteFromClipboard == nil {
|
||||||
|
t.pasteFromClipboard = func() string {
|
||||||
|
return t.clipboard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// replace deletes a range of text and inserts the given text at that position.
|
// replace deletes a range of text and inserts the given text at that position.
|
||||||
// If the resulting text would exceed the maximum length, the function does not
|
// If the resulting text would exceed the maximum length, the function does not
|
||||||
// do anything. The function returns the new position of the deleted/inserted
|
// do anything. The function returns the new position of the deleted/inserted
|
||||||
|
@ -1236,6 +1275,32 @@ func (t *TextArea) getSelection() ([3]int, [3]int, int) {
|
||||||
return from, to, row
|
return from, to, row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSelectedText returns the text of the current selection.
|
||||||
|
func (t *TextArea) getSelectedText() string {
|
||||||
|
var text strings.Builder
|
||||||
|
|
||||||
|
from, to, _ := t.getSelection()
|
||||||
|
for from[0] != to[0] {
|
||||||
|
span := t.spans[from[0]]
|
||||||
|
if span.length < 0 {
|
||||||
|
text.WriteString(t.initialText[span.offset+from[1] : span.offset-span.length])
|
||||||
|
} else {
|
||||||
|
text.WriteString(t.editText.String()[span.offset+from[1] : span.offset+span.length])
|
||||||
|
}
|
||||||
|
from[0], from[1] = span.next, 0
|
||||||
|
}
|
||||||
|
if from[0] != 1 && from[1] < to[1] {
|
||||||
|
span := t.spans[from[0]]
|
||||||
|
if span.length < 0 {
|
||||||
|
text.WriteString(t.initialText[span.offset+from[1] : span.offset+to[1]])
|
||||||
|
} else {
|
||||||
|
text.WriteString(t.editText.String()[span.offset+from[1] : span.offset+to[1]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.String()
|
||||||
|
}
|
||||||
|
|
||||||
// InputHandler returns the handler for this primitive.
|
// InputHandler returns the handler for this primitive.
|
||||||
func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||||
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||||
|
@ -1350,9 +1415,8 @@ func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
||||||
t.selectionStart = t.cursor
|
t.selectionStart = t.cursor
|
||||||
}
|
}
|
||||||
case tcell.KeyEnter: // Insert a newline.
|
case tcell.KeyEnter: // Insert a newline.
|
||||||
from, to, _ := t.getSelection()
|
from, to, row := t.getSelection()
|
||||||
t.cursor.pos = t.replace(from, to, NewLine)
|
t.cursor.pos = t.replace(from, to, NewLine)
|
||||||
row := t.cursor.row
|
|
||||||
t.cursor.row = -1
|
t.cursor.row = -1
|
||||||
t.truncateLines(row - 1)
|
t.truncateLines(row - 1)
|
||||||
t.clampToCursor(row)
|
t.clampToCursor(row)
|
||||||
|
@ -1373,12 +1437,12 @@ func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Other keys are simply accepted as regular characters.
|
// Other keys are simply accepted as regular characters.
|
||||||
from, to, _ := t.getSelection()
|
from, to, row := t.getSelection()
|
||||||
t.cursor.pos = t.replace(from, to, string(event.Rune()))
|
t.cursor.pos = t.replace(from, to, string(event.Rune()))
|
||||||
row := t.cursor.row
|
|
||||||
t.cursor.row = -1
|
t.cursor.row = -1
|
||||||
t.truncateLines(row - 1)
|
t.truncateLines(row - 1)
|
||||||
t.clampToCursor(row)
|
t.clampToCursor(row)
|
||||||
|
t.selectionStart = t.cursor
|
||||||
}
|
}
|
||||||
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete backwards. tcell.KeyBackspace is the same as tcell.CtrlH.
|
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete backwards. tcell.KeyBackspace is the same as tcell.CtrlH.
|
||||||
from, to, row := t.getSelection()
|
from, to, row := t.getSelection()
|
||||||
|
@ -1463,6 +1527,28 @@ func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
||||||
case tcell.KeyCtrlU: // Delete the current line.
|
case tcell.KeyCtrlU: // Delete the current line.
|
||||||
t.deleteLine()
|
t.deleteLine()
|
||||||
t.selectionStart = t.cursor
|
t.selectionStart = t.cursor
|
||||||
|
case tcell.KeyCtrlQ: // Copy to clipboard.
|
||||||
|
if t.cursor != t.selectionStart {
|
||||||
|
t.copyToClipboard(t.getSelectedText())
|
||||||
|
t.selectionStart = t.cursor
|
||||||
|
}
|
||||||
|
case tcell.KeyCtrlX: // Cut to clipboard.
|
||||||
|
if t.cursor != t.selectionStart {
|
||||||
|
t.copyToClipboard(t.getSelectedText())
|
||||||
|
from, to, row := t.getSelection()
|
||||||
|
t.cursor.pos = t.replace(from, to, "")
|
||||||
|
t.cursor.row = -1
|
||||||
|
t.truncateLines(row - 1)
|
||||||
|
t.clampToCursor(row)
|
||||||
|
t.selectionStart = t.cursor
|
||||||
|
}
|
||||||
|
case tcell.KeyCtrlV: // Paste from clipboard.
|
||||||
|
from, to, row := t.getSelection()
|
||||||
|
t.cursor.pos = t.replace(from, to, t.pasteFromClipboard())
|
||||||
|
t.cursor.row = -1
|
||||||
|
t.truncateLines(row - 1)
|
||||||
|
t.clampToCursor(row)
|
||||||
|
t.selectionStart = t.cursor
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue