Add TextArea (WIP)

This commit is contained in:
Trevor Slocum 2021-07-04 01:09:07 -07:00
parent 9c993d8c7e
commit cd2edc8176
4 changed files with 136 additions and 2 deletions

View File

@ -63,6 +63,7 @@ func main() {
TextView1,
TextView2,
InputField,
TextArea,
Slider,
Form,
Table,

View File

@ -0,0 +1,13 @@
package main
import (
"code.rocketnine.space/tslocum/cview"
)
// TextArea demonstrates the TextArea.
func TextArea(nextSlide func()) (title string, info string, content cview.Primitive) {
t := cview.NewTextArea()
t.SetBorder(true)
t.SetTitle("Multi-line text input")
return "TextArea", "", Center(44, 16, t)
}

81
textarea.go Normal file
View File

@ -0,0 +1,81 @@
package cview
import (
"sync"
"github.com/gdamore/tcell/v2"
)
// TextArea is a multi-line text input.
type TextArea struct {
*TextView
setFocus func(Primitive)
sync.RWMutex
}
// NewTextArea returns a new TextArea object.
func NewTextArea() *TextArea {
t := &TextArea{
TextView: NewTextView(),
}
t.TextView.SetShowCursor(true)
return t
}
// Draw draws this primitive onto the screen.
func (t *TextArea) Draw(screen tcell.Screen) {
if !t.GetVisible() {
return
}
t.Box.Draw(screen)
t.TextView.Draw(screen)
}
// InputHandler returns the handler for this primitive.
func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
if t.setFocus == nil {
t.setFocus = setFocus
}
add := func(r rune) bool {
t.Write([]byte(string(r)))
return true
}
if event.Key() == tcell.KeyRune {
add(event.Rune())
return
} else if event.Key() == tcell.KeyEnter {
add('\n')
return
} else if event.Key() == tcell.KeyBackspace2 {
b := t.GetBytes(false)
t.SetBytes(b[:len(b)-1])
return
}
t.TextView.InputHandler()(event, setFocus)
})
}
// MouseHandler returns the mouse handler for this primitive.
func (t *TextArea) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
if t.setFocus == nil {
t.setFocus = setFocus
}
x, y := event.Position()
if !t.InRect(x, y) {
return false, nil
}
return t.TextView.MouseHandler()(action, event, setFocus)
})
}

View File

@ -196,6 +196,9 @@ type TextView struct {
// If set to true, region tags can be used to define regions.
regions bool
// If set to true, a cursor is drawn.
showCursor bool
// A temporary flag which, when true, will automatically bring the current
// highlight(s) into the visible screen.
scrollToHighlights bool
@ -731,6 +734,14 @@ func (t *TextView) GetRegionText(regionID string) string {
return escapePattern.ReplaceAllString(buffer.String(), `[$1$2]`)
}
// SetShowCursor sets a flag controlling whether a cursor is drawn.
func (t *TextView) SetShowCursor(showCursor bool) {
t.Lock()
defer t.Unlock()
t.showCursor = showCursor
}
// Focus is called when this primitive receives focus.
func (t *TextView) Focus(delegate func(p Primitive)) {
t.Lock()
@ -818,7 +829,7 @@ func (t *TextView) write(p []byte) (n int, err error) {
return len(p), nil
}
// SetWrapWidth set the maximum width of lines when wrapping is enabled.
// SetWrapWidth sets the maximum width of lines when wrapping is enabled.
// When set to 0 the width of the TextView is used.
func (t *TextView) SetWrapWidth(width int) {
t.Lock()
@ -827,7 +838,7 @@ func (t *TextView) SetWrapWidth(width int) {
t.wrapWidth = width
}
// SetReindexBuffer set a flag controlling whether the buffer is reindexed when
// SetReindexBuffer sets a flag controlling whether the buffer is reindexed when
// it is modified. This improves the performance of TextViews whose contents
// always have line-breaks in the same location. This must be called after the
// buffer has been indexed.
@ -1022,9 +1033,18 @@ func (t *TextView) reindexBuffer(width int) {
}
}
func (t *TextView) updateCursorPos(screen tcell.Screen, x int, y int) {
if !t.showCursor || x == -1 {
screen.HideCursor()
return
}
screen.ShowCursor(x, y)
}
// Draw draws this primitive onto the screen.
func (t *TextView) Draw(screen tcell.Screen) {
if !t.GetVisible() {
t.updateCursorPos(screen, -1, -1)
return
}
@ -1036,6 +1056,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
// Get the available size.
x, y, width, height := t.GetInnerRect()
if height == 0 {
t.updateCursorPos(screen, -1, -1)
return
}
t.pageSize = height
@ -1077,6 +1098,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
// If we don't have an index, there's nothing to draw.
if t.index == nil {
t.updateCursorPos(screen, x, y)
return
}
@ -1152,6 +1174,8 @@ func (t *TextView) Draw(screen tcell.Screen) {
verticalOffset = height - len(t.index)
}
}
var lastLine int
var lastPosX int
// Draw the buffer.
defaultStyle := tcell.StyleDefault.Foreground(t.textColor).Background(t.backgroundColor)
@ -1161,6 +1185,9 @@ func (t *TextView) Draw(screen tcell.Screen) {
break
}
lastLine = line
lastPosX = -1
// Get the text for this line.
index := t.index[line]
text := t.buffer[index.Line][index.Pos:index.NextPos]
@ -1293,6 +1320,8 @@ func (t *TextView) Draw(screen tcell.Screen) {
}
}
lastPosX = posX
// Advance.
posX += screenWidth
return false
@ -1300,6 +1329,16 @@ func (t *TextView) Draw(screen tcell.Screen) {
}
}
cursorX, cursorY := x+lastPosX+1, y+lastLine-t.lineOffset
if cursorX >= x+width {
cursorX = x
cursorY++
if cursorY > height {
cursorX = -1
}
}
t.updateCursorPos(screen, cursorX, cursorY)
// If this view is not scrollable, we'll purge the buffer of lines that have
// scrolled out of view.
if !t.scrollable && t.lineOffset > 0 {