Enabled bracketed pasting.

pull/952/head
Oliver 3 months ago
parent 007cbb1d13
commit c9421b4bd9

@ -1,6 +1,7 @@
package tview package tview
import ( import (
"strings"
"sync" "sync"
"time" "time"
@ -83,6 +84,9 @@ type Application struct {
// Set to true if mouse events are enabled. // Set to true if mouse events are enabled.
enableMouse bool enableMouse bool
// Set to true if paste events are enabled.
enablePaste bool
// An optional capture function which receives a key event and returns the // An optional capture function which receives a key event and returns the
// event to be forwarded to the default input handler (nil if nothing should // event to be forwarded to the default input handler (nil if nothing should
// be forwarded). // be forwarded).
@ -145,6 +149,9 @@ func NewApplication() *Application {
// forward the Ctrl-C event to primitives down the hierarchy, return a new // forward the Ctrl-C event to primitives down the hierarchy, return a new
// key event with the same key and modifiers, e.g. // key event with the same key and modifiers, e.g.
// tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone). // tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone).
//
// Pasted key events are not forwarded to the input capture function if pasting
// is enabled (see [Application.EnablePaste]).
func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application { func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
a.inputCapture = capture a.inputCapture = capture
return a return a
@ -216,6 +223,26 @@ func (a *Application) EnableMouse(enable bool) *Application {
return a return a
} }
// EnablePaste enables the capturing of paste events or disables them (if
// "false" is provided). This must be supported by the terminal.
//
// Widgets won't interpret paste events for navigation or selection purposes.
// Paste events are typically only used to insert a block of text into an
// [InputField] or a [TextArea].
func (a *Application) EnablePaste(enable bool) *Application {
a.Lock()
defer a.Unlock()
if enable != a.enablePaste && a.screen != nil {
if enable {
a.screen.EnablePaste()
} else {
a.screen.DisablePaste()
}
}
a.enablePaste = enable
return a
}
// Run starts the application and thus the event loop. This function returns // Run starts the application and thus the event loop. This function returns
// when Stop() was called. // when Stop() was called.
func (a *Application) Run() error { func (a *Application) Run() error {
@ -239,6 +266,13 @@ func (a *Application) Run() error {
} }
if a.enableMouse { if a.enableMouse {
a.screen.EnableMouse() a.screen.EnableMouse()
} else {
a.screen.DisableMouse()
}
if a.enablePaste {
a.screen.EnablePaste()
} else {
a.screen.DisablePaste()
} }
} }
@ -283,7 +317,7 @@ func (a *Application) Run() error {
screen = <-a.screenReplacement screen = <-a.screenReplacement
if screen == nil { if screen == nil {
// No new screen. We're done. // No new screen. We're done.
a.QueueEvent(nil) a.QueueEvent(nil) // Stop the event loop.
return return
} }
@ -291,6 +325,7 @@ func (a *Application) Run() error {
a.Lock() a.Lock()
a.screen = screen a.screen = screen
enableMouse := a.enableMouse enableMouse := a.enableMouse
enablePaste := a.enablePaste
a.Unlock() a.Unlock()
// Initialize and draw this screen. // Initialize and draw this screen.
@ -299,15 +334,27 @@ func (a *Application) Run() error {
} }
if enableMouse { if enableMouse {
screen.EnableMouse() screen.EnableMouse()
} else {
screen.DisableMouse()
}
if enablePaste {
screen.EnablePaste()
} else {
screen.DisablePaste()
} }
a.draw() a.draw()
} }
}() }()
// Start event loop. // Start event loop.
var (
pasteBuffer strings.Builder
pasting bool // Set to true while we receive paste key events.
)
EventLoop: EventLoop:
for { for {
select { select {
// If we received an event, handle it.
case event := <-a.events: case event := <-a.events:
if event == nil { if event == nil {
break EventLoop break EventLoop
@ -315,6 +362,19 @@ EventLoop:
switch event := event.(type) { switch event := event.(type) {
case *tcell.EventKey: case *tcell.EventKey:
// If we are pasting, collect runes, nothing else.
if pasting {
switch event.Key() {
case tcell.KeyRune:
pasteBuffer.WriteRune(event.Rune())
case tcell.KeyEnter:
pasteBuffer.WriteRune('\n')
case tcell.KeyTab:
pasteBuffer.WriteRune('\t')
}
break
}
a.RLock() a.RLock()
root := a.root root := a.root
inputCapture := a.inputCapture inputCapture := a.inputCapture
@ -327,7 +387,7 @@ EventLoop:
event = inputCapture(event) event = inputCapture(event)
if event == nil { if event == nil {
a.draw() a.draw()
continue // Don't forward event. break // Don't forward event.
} }
draw = true draw = true
} }
@ -352,6 +412,30 @@ EventLoop:
if draw { if draw {
a.draw() a.draw()
} }
case *tcell.EventPaste:
if !a.enablePaste {
break
}
if event.Start() {
pasting = true
pasteBuffer.Reset()
} else if event.End() {
pasting = false
a.RLock()
root := a.root
a.RUnlock()
if root != nil && root.HasFocus() && pasteBuffer.Len() > 0 {
// Pass paste event to the root primitive.
if handler := root.PasteHandler(); handler != nil {
handler(pasteBuffer.String(), func(p Primitive) {
a.SetFocus(p)
})
}
// Redraw.
a.draw()
}
}
case *tcell.EventResize: case *tcell.EventResize:
if time.Since(lastRedraw) < redrawPause { if time.Since(lastRedraw) < redrawPause {
if redrawTimer != nil { if redrawTimer != nil {
@ -365,7 +449,7 @@ EventLoop:
screen := a.screen screen := a.screen
a.RUnlock() a.RUnlock()
if screen == nil { if screen == nil {
continue break
} }
lastRedraw = time.Now() lastRedraw = time.Now()
screen.Clear() screen.Clear()

@ -153,8 +153,8 @@ func (b *Box) GetDrawFunc() func(screen tcell.Screen, x, y, width, height int) (
return b.draw return b.draw
} }
// WrapInputHandler wraps an input handler (see InputHandler()) with the // WrapInputHandler wraps an input handler (see [Box.InputHandler]) with the
// functionality to capture input (see SetInputCapture()) before passing it // functionality to capture input (see [Box.SetInputCapture]) before passing it
// on to the provided (default) input handler. // on to the provided (default) input handler.
// //
// This is only meant to be used by subclassing primitives. // This is only meant to be used by subclassing primitives.
@ -169,11 +169,25 @@ func (b *Box) WrapInputHandler(inputHandler func(*tcell.EventKey, func(p Primiti
} }
} }
// InputHandler returns nil. // InputHandler returns nil. Box has no default input handling.
func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return b.WrapInputHandler(nil) return b.WrapInputHandler(nil)
} }
// WrapPasteHandler wraps a paste handler (see [Box.PasteHandler]).
func (b *Box) WrapPasteHandler(pasteHandler func(string, func(p Primitive))) func(string, func(p Primitive)) {
return func(text string, setFocus func(p Primitive)) {
if pasteHandler != nil {
pasteHandler(text, setFocus)
}
}
}
// PasteHandler returns nil. Box has no default paste handling.
func (b *Box) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return b.WrapPasteHandler(nil)
}
// SetInputCapture installs a function which captures key events before they are // SetInputCapture installs a function which captures key events before they are
// forwarded to the primitive's default key event handler. This function can // forwarded to the primitive's default key event handler. This function can
// then choose to forward that key event (or a different one) to the default // then choose to forward that key event (or a different one) to the default
@ -184,6 +198,9 @@ func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primiti
// //
// This function can also be used on container primitives (like Flex, Grid, or // This function can also be used on container primitives (like Flex, Grid, or
// Form) as keyboard events will be handed down until they are handled. // Form) as keyboard events will be handed down until they are handled.
//
// Pasted key events are not forwarded to the input capture function if pasting
// is enabled (see [Application.EnablePaste]).
func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box { func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box {
b.inputCapture = capture b.inputCapture = capture
return b return b
@ -195,8 +212,8 @@ func (b *Box) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
return b.inputCapture return b.inputCapture
} }
// WrapMouseHandler wraps a mouse event handler (see MouseHandler()) with the // WrapMouseHandler wraps a mouse event handler (see [Box.MouseHandler]) with the
// functionality to capture mouse events (see SetMouseCapture()) before passing // functionality to capture mouse events (see [Box.SetMouseCapture]) before passing
// them on to the provided (default) event handler. // them on to the provided (default) event handler.
// //
// This is only meant to be used by subclassing primitives. // This is only meant to be used by subclassing primitives.
@ -212,7 +229,7 @@ func (b *Box) WrapMouseHandler(mouseHandler func(MouseAction, *tcell.EventMouse,
} }
} }
// MouseHandler returns nil. // MouseHandler returns nil. Box has no default mouse handling.
func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
if action == MouseLeftDown && b.InRect(event.Position()) { if action == MouseLeftDown && b.InRect(event.Position()) {

@ -20,7 +20,7 @@ func main() {
app.Stop() app.Stop()
}) })
form.SetBorder(true).SetTitle("Enter some data").SetTitleAlign(tview.AlignLeft) form.SetBorder(true).SetTitle("Enter some data").SetTitleAlign(tview.AlignLeft)
if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil { if err := app.SetRoot(form, true).EnableMouse(true).EnablePaste(true).Run(); err != nil {
panic(err) panic(err)
} }
} }

@ -16,7 +16,7 @@ func main() {
SetDoneFunc(func(key tcell.Key) { SetDoneFunc(func(key tcell.Key) {
app.Stop() app.Stop()
}) })
if err := app.SetRoot(inputField, true).EnableMouse(true).Run(); err != nil { if err := app.SetRoot(inputField, true).EnableMouse(true).EnablePaste(true).Run(); err != nil {
panic(err) panic(err)
} }
} }

@ -1,7 +1,7 @@
/* /*
A presentation of the tview package, implemented with tview. A presentation of the tview package, implemented with tview.
Navigation # Navigation
The presentation will advance to the next slide when the primitive demonstrated The presentation will advance to the next slide when the primitive demonstrated
in the current slide is left (usually by hitting Enter or Escape). Additionally, in the current slide is left (usually by hitting Enter or Escape). Additionally,
@ -97,7 +97,7 @@ func main() {
}) })
// Start the application. // Start the application.
if err := app.SetRoot(layout, true).EnableMouse(true).Run(); err != nil { if err := app.SetRoot(layout, true).EnableMouse(true).EnablePaste(true).Run(); err != nil {
panic(err) panic(err)
} }
} }

@ -35,7 +35,7 @@ func main() {
updateInfos() updateInfos()
mainView := tview.NewGrid(). mainView := tview.NewGrid().
SetRows(3, 0). SetRows(0, 1).
AddItem(textArea, 0, 0, 1, 2, 0, 0, true). AddItem(textArea, 0, 0, 1, 2, 0, 0, true).
AddItem(helpInfo, 1, 0, 1, 1, 0, 0, false). AddItem(helpInfo, 1, 0, 1, 1, 0, 0, false).
AddItem(position, 1, 1, 1, 1, 0, 0, false) AddItem(position, 1, 1, 1, 1, 0, 0, false)
@ -128,8 +128,7 @@ Double-click to select a word.
return event return event
}) })
if err := app.SetRoot(pages, if err := app.SetRoot(pages, true).EnableMouse(true).EnablePaste(true).Run(); err != nil {
true).EnableMouse(true).Run(); err != nil {
panic(err) panic(err)
} }
} }

@ -259,3 +259,17 @@ func (f *Flex) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
} }
}) })
} }
// PasteHandler returns the handler for this primitive.
func (f *Flex) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return f.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
for _, item := range f.items {
if item.Item != nil && item.Item.HasFocus() {
if handler := item.Item.PasteHandler(); handler != nil {
handler(pastedText, setFocus)
return
}
}
}
})
}

@ -867,3 +867,26 @@ func (f *Form) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
} }
}) })
} }
// PasteHandler returns the handler for this primitive.
func (f *Form) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return f.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
for _, item := range f.items {
if item != nil && item.HasFocus() {
if handler := item.PasteHandler(); handler != nil {
handler(pastedText, setFocus)
return
}
}
}
for _, button := range f.buttons {
if button.HasFocus() {
if handler := button.PasteHandler(); handler != nil {
handler(pastedText, setFocus)
return
}
}
}
})
}

@ -220,3 +220,16 @@ func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primi
} }
}) })
} }
// PasteHandler returns the handler for this primitive.
func (f *Frame) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return f.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
if f.primitive == nil {
return
}
if handler := f.primitive.PasteHandler(); handler != nil {
handler(pastedText, setFocus)
return
}
})
}

@ -266,55 +266,6 @@ func (g *Grid) HasFocus() bool {
return g.Box.HasFocus() return g.Box.HasFocus()
} }
// InputHandler returns the handler for this primitive.
func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return g.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
if !g.hasFocus {
// Pass event on to child primitive.
for _, item := range g.items {
if item != nil && item.Item.HasFocus() {
if handler := item.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
}
return
}
// Process our own key events if we have direct focus.
switch event.Key() {
case tcell.KeyRune:
switch event.Rune() {
case 'g':
g.rowOffset, g.columnOffset = 0, 0
case 'G':
g.rowOffset = math.MaxInt32
case 'j':
g.rowOffset++
case 'k':
g.rowOffset--
case 'h':
g.columnOffset--
case 'l':
g.columnOffset++
}
case tcell.KeyHome:
g.rowOffset, g.columnOffset = 0, 0
case tcell.KeyEnd:
g.rowOffset = math.MaxInt32
case tcell.KeyUp:
g.rowOffset--
case tcell.KeyDown:
g.rowOffset++
case tcell.KeyLeft:
g.columnOffset--
case tcell.KeyRight:
g.columnOffset++
}
})
}
// Draw draws this primitive onto the screen. // Draw draws this primitive onto the screen.
func (g *Grid) Draw(screen tcell.Screen) { func (g *Grid) Draw(screen tcell.Screen) {
g.Box.DrawForSubclass(screen, g) g.Box.DrawForSubclass(screen, g)
@ -714,3 +665,66 @@ func (g *Grid) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
return return
}) })
} }
// InputHandler returns the handler for this primitive.
func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return g.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
if !g.hasFocus {
// Pass event on to child primitive.
for _, item := range g.items {
if item != nil && item.Item.HasFocus() {
if handler := item.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
}
return
}
// Process our own key events if we have direct focus.
switch event.Key() {
case tcell.KeyRune:
switch event.Rune() {
case 'g':
g.rowOffset, g.columnOffset = 0, 0
case 'G':
g.rowOffset = math.MaxInt32
case 'j':
g.rowOffset++
case 'k':
g.rowOffset--
case 'h':
g.columnOffset--
case 'l':
g.columnOffset++
}
case tcell.KeyHome:
g.rowOffset, g.columnOffset = 0, 0
case tcell.KeyEnd:
g.rowOffset = math.MaxInt32
case tcell.KeyUp:
g.rowOffset--
case tcell.KeyDown:
g.rowOffset++
case tcell.KeyLeft:
g.columnOffset--
case tcell.KeyRight:
g.columnOffset++
}
})
}
// PasteHandler returns the handler for this primitive.
func (g *Grid) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return g.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
for _, item := range g.items {
if item != nil && item.Item.HasFocus() {
if handler := item.Item.PasteHandler(); handler != nil {
handler(pastedText, setFocus)
return
}
}
}
})
}

@ -63,6 +63,11 @@ var (
// //
// - Tab, BackTab, Enter, Escape: Finish editing. // - Tab, BackTab, Enter, Escape: Finish editing.
// //
// Note that while pressing Tab or Enter is intercepted by the input field, it
// is possible to paste such characters into the input field, possibly resulting
// in multi-line input. You can use [InputField.SetAcceptanceFunc] to prevent
// this.
//
// If autocomplete functionality is configured: // If autocomplete functionality is configured:
// //
// - Down arrow: Open the autocomplete drop-down. // - Down arrow: Open the autocomplete drop-down.
@ -386,6 +391,8 @@ func (i *InputField) Autocomplete() *InputField {
// This package defines a number of variables prefixed with InputField which may // This package defines a number of variables prefixed with InputField which may
// be used for common input (e.g. numbers, maximum text length). See for example // be used for common input (e.g. numbers, maximum text length). See for example
// [InputFieldInteger]. // [InputFieldInteger].
//
// When text is pasted, lastChar is 0.
func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *InputField { func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *InputField {
i.accept = handler i.accept = handler
return i return i
@ -587,6 +594,11 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
i.autocompleteListMutex.Lock() i.autocompleteListMutex.Lock()
case tcell.KeyEnter, tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab: case tcell.KeyEnter, tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab:
finish(key) finish(key)
case tcell.KeyCtrlV:
if i.accept != nil && !i.accept(i.textArea.getTextBeforeCursor()+i.textArea.GetClipboardText()+i.textArea.getTextAfterCursor(), 0) {
return
}
i.textArea.InputHandler()(event, setFocus)
case tcell.KeyRune: case tcell.KeyRune:
if event.Modifiers()&tcell.ModAlt == 0 && i.accept != nil { if event.Modifiers()&tcell.ModAlt == 0 && i.accept != nil {
// Check if this rune is accepted. // Check if this rune is accepted.
@ -660,3 +672,28 @@ func (i *InputField) MouseHandler() func(action MouseAction, event *tcell.EventM
return return
}) })
} }
// PasteHandler returns the handler for this primitive.
func (i *InputField) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return i.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
// Input field may be disabled.
if i.textArea.GetDisabled() {
return
}
// The autocomplete drop down may be open.
i.autocompleteListMutex.Lock()
defer i.autocompleteListMutex.Unlock()
if i.autocompleteList != nil {
return
}
// We may not accept this text.
if i.accept != nil && !i.accept(i.textArea.getTextBeforeCursor()+pastedText+i.textArea.getTextAfterCursor(), 0) {
return
}
// Forward the pasted text to the text area.
i.textArea.PasteHandler()(pastedText, setFocus)
})
}

@ -315,3 +315,17 @@ func (p *Pages) InputHandler() func(event *tcell.EventKey, setFocus func(p Primi
} }
}) })
} }
// PasteHandler returns the handler for this primitive.
func (p *Pages) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return p.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
for _, page := range p.pages {
if page.Item.HasFocus() {
if handler := page.Item.PasteHandler(); handler != nil {
handler(pastedText, setFocus)
return
}
}
}
})
}

@ -55,4 +55,15 @@ type Primitive interface {
// subclass from Box, it is recommended that you wrap your handler using // subclass from Box, it is recommended that you wrap your handler using
// Box.WrapMouseHandler() so you inherit that functionality. // Box.WrapMouseHandler() so you inherit that functionality.
MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive)
// PasteHandler returns a handler which receives pasted text.
// It is called by the Application class.
//
// A value of nil may also be returned to stop the downward propagation of
// paste events.
//
// The Box class may provide functionality to intercept paste events in the
// future. If you subclass from Box, it is recommended that you wrap your
// handler using Box.WrapPasteHandler() so you inherit that functionality.
PasteHandler() func(text string, setFocus func(p Primitive))
} }

@ -165,13 +165,15 @@ type textAreaUndoItem struct {
// operating system's key bindings for copy+paste functionality may not have the // 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. Pasting text // expected effect as tview will not be able to handle these keys. Pasting text
// using your operating system's or terminal's own methods may be very slow as // using your operating system's or terminal's own methods may be very slow as
// each character will be pasted individually. // each character will be pasted individually. However, some terminals support
// pasting text blocks which is supported by the text area, see
// [Application.EnablePaste] for details.
// //
// The default clipboard is an internal text buffer, i.e. the operating system's // The default clipboard is an internal text buffer local to this text area
// clipboard is not used. If you want to implement your own clipboard (or make // instance, i.e. the operating system's clipboard is not used. If you want to
// use of your operating system's clipboard), you can use // implement your own clipboard (or make use of your operating system's
// [TextArea.SetClipboard] which provides all the functionality needed to // clipboard), you can use [TextArea.SetClipboard] which provides all the
// implement your own clipboard. // functionality needed to implement your own clipboard.
// //
// The text area also supports Undo: // The text area also supports Undo:
// //
@ -917,7 +919,8 @@ func (t *TextArea) SetOffset(row, column int) *TextArea {
// retrieve text from the clipboard (pasteFromClipboard). // retrieve text from the clipboard (pasteFromClipboard).
// //
// Providing nil values will cause the default clipboard implementation to be // Providing nil values will cause the default clipboard implementation to be
// used. // used. Note that the default clipboard is local to this text area instance.
// Copying text to other widgets will not work.
func (t *TextArea) SetClipboard(copyToClipboard func(string), pasteFromClipboard func() string) *TextArea { func (t *TextArea) SetClipboard(copyToClipboard func(string), pasteFromClipboard func() string) *TextArea {
t.copyToClipboard = copyToClipboard t.copyToClipboard = copyToClipboard
if t.copyToClipboard == nil { if t.copyToClipboard == nil {
@ -936,6 +939,12 @@ func (t *TextArea) SetClipboard(copyToClipboard func(string), pasteFromClipboard
return t return t
} }
// GetClipboardText returns the current text of the clipboard by calling the
// pasteFromClipboard function set with [TextArea.SetClipboard].
func (t *TextArea) GetClipboardText() string {
return t.pasteFromClipboard()
}
// SetChangedFunc sets a handler which is called whenever the text of the text // SetChangedFunc sets a handler which is called whenever the text of the text
// area has changed. // area has changed.
func (t *TextArea) SetChangedFunc(handler func()) *TextArea { func (t *TextArea) SetChangedFunc(handler func()) *TextArea {
@ -1218,7 +1227,7 @@ func (t *TextArea) Draw(screen tcell.Screen) {
} }
}() }()
// No text / placeholder. // No text, show placeholder.
if t.length == 0 { if t.length == 0 {
t.lastHeight, t.lastWidth = height, width t.lastHeight, t.lastWidth = height, width
t.cursor.row, t.cursor.column, t.cursor.actualColumn, t.cursor.pos = 0, 0, 0, [3]int{1, 0, -1} t.cursor.row, t.cursor.column, t.cursor.actualColumn, t.cursor.pos = 0, 0, 0, [3]int{1, 0, -1}
@ -1280,6 +1289,13 @@ func (t *TextArea) Draw(screen tcell.Screen) {
} }
} }
// Selected tabs are a bit special.
if cluster == "\t" && style == t.selectedStyle {
for colX := 0; colX < clusterWidth && posX+colX-columnOffset < width; colX++ {
screen.SetContent(x+posX+colX-columnOffset, y+posY, ' ', nil, style)
}
}
// Draw character. // Draw character.
if posX+clusterWidth-columnOffset <= width && posX-columnOffset >= 0 && clusterWidth > 0 { if posX+clusterWidth-columnOffset <= width && posX-columnOffset >= 0 && clusterWidth > 0 {
screen.SetContent(x+posX-columnOffset, y+posY, runes[0], runes[1:], style) screen.SetContent(x+posX-columnOffset, y+posY, runes[0], runes[1:], style)
@ -2414,3 +2430,15 @@ func (t *TextArea) MouseHandler() func(action MouseAction, event *tcell.EventMou
return return
}) })
} }
// PasteHandler returns the handler for this primitive.
func (t *TextArea) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
return t.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
from, to, row := t.getSelection()
t.cursor.pos = t.replace(from, to, pastedText, false)
t.cursor.row = -1
t.truncateLines(row - 1)
t.findCursor(true, row)
t.selectionStart = t.cursor
})
}

Loading…
Cancel
Save