forked from tslocum/cview
Store InputField text data as []byte instead of string
parent
d5edb975b6
commit
2763609e05
116
inputfield.go
116
inputfield.go
|
@ -1,9 +1,9 @@
|
|||
package cview
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
|
@ -33,13 +33,13 @@ type InputField struct {
|
|||
*Box
|
||||
|
||||
// The text that was entered.
|
||||
text string
|
||||
text []byte
|
||||
|
||||
// The text to be displayed before the input area.
|
||||
label string
|
||||
label []byte
|
||||
|
||||
// The text to be displayed in the input area when "text" is empty.
|
||||
placeholder string
|
||||
placeholder []byte
|
||||
|
||||
// The label color.
|
||||
labelColor tcell.Color
|
||||
|
@ -84,7 +84,7 @@ type InputField struct {
|
|||
fieldNoteTextColor tcell.Color
|
||||
|
||||
// The note to show below the input field.
|
||||
fieldNote string
|
||||
fieldNote []byte
|
||||
|
||||
// The screen width of the label area. A value of 0 means use the width of
|
||||
// the label text.
|
||||
|
@ -113,7 +113,7 @@ type InputField struct {
|
|||
autocompleteList *List
|
||||
|
||||
// The suggested completion of the current autocomplete ListItem.
|
||||
autocompleteListSuggestion string
|
||||
autocompleteListSuggestion []byte
|
||||
|
||||
// An optional function which may reject the last character that was entered.
|
||||
accept func(text string, ch rune) bool
|
||||
|
@ -164,7 +164,7 @@ func NewInputField() *InputField {
|
|||
func (i *InputField) SetText(text string) *InputField {
|
||||
i.Lock()
|
||||
|
||||
i.text = text
|
||||
i.text = []byte(text)
|
||||
i.cursorPos = len(text)
|
||||
if i.changed != nil {
|
||||
i.Unlock()
|
||||
|
@ -181,7 +181,7 @@ func (i *InputField) GetText() string {
|
|||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.text
|
||||
return string(i.text)
|
||||
}
|
||||
|
||||
// SetLabel sets the text to be displayed before the input area.
|
||||
|
@ -189,7 +189,7 @@ func (i *InputField) SetLabel(label string) *InputField {
|
|||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
i.label = label
|
||||
i.label = []byte(label)
|
||||
return i
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ func (i *InputField) GetLabel() string {
|
|||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.label
|
||||
return string(i.label)
|
||||
}
|
||||
|
||||
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
|
||||
|
@ -216,7 +216,7 @@ func (i *InputField) SetPlaceholder(text string) *InputField {
|
|||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
i.placeholder = text
|
||||
i.placeholder = []byte(text)
|
||||
return i
|
||||
}
|
||||
|
||||
|
@ -358,7 +358,7 @@ func (i *InputField) SetFieldNote(note string) *InputField {
|
|||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
i.fieldNote = note
|
||||
i.fieldNote = []byte(note)
|
||||
return i
|
||||
}
|
||||
|
||||
|
@ -367,7 +367,7 @@ func (i *InputField) ResetFieldNote() *InputField {
|
|||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
i.fieldNote = ""
|
||||
i.fieldNote = nil
|
||||
return i
|
||||
}
|
||||
|
||||
|
@ -393,7 +393,7 @@ func (i *InputField) GetFieldWidth() int {
|
|||
func (i *InputField) GetFieldHeight() int {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
if i.fieldNote == "" {
|
||||
if len(i.fieldNote) == 0 {
|
||||
return 1
|
||||
}
|
||||
return 2
|
||||
|
@ -457,12 +457,12 @@ func (i *InputField) Autocomplete() *InputField {
|
|||
i.Unlock()
|
||||
|
||||
// Do we have any autocomplete entries?
|
||||
entries := i.autocomplete(i.text)
|
||||
entries := i.autocomplete(string(i.text))
|
||||
if len(entries) == 0 {
|
||||
// No entries, no list.
|
||||
i.Lock()
|
||||
i.autocompleteList = nil
|
||||
i.autocompleteListSuggestion = ""
|
||||
i.autocompleteListSuggestion = nil
|
||||
i.Unlock()
|
||||
return i
|
||||
}
|
||||
|
@ -488,7 +488,7 @@ func (i *InputField) Autocomplete() *InputField {
|
|||
for index, entry := range entries {
|
||||
entry.enabled = true
|
||||
i.autocompleteList.AddItem(entry)
|
||||
if currentEntry < 0 && entry.GetMainText() == i.text {
|
||||
if currentEntry < 0 && entry.GetMainText() == string(i.text) {
|
||||
currentEntry = index
|
||||
}
|
||||
}
|
||||
|
@ -505,14 +505,14 @@ func (i *InputField) Autocomplete() *InputField {
|
|||
// autocompleteChanged gets called when another item in the
|
||||
// autocomplete list has been selected.
|
||||
func (i *InputField) autocompleteChanged(_ int, item *ListItem) {
|
||||
mainText := item.GetMainText()
|
||||
secondaryText := item.GetSecondaryText()
|
||||
if len(secondaryText) > 0 && len(i.text) < len(secondaryText) {
|
||||
mainText := item.GetMainBytes()
|
||||
secondaryText := item.GetSecondaryBytes()
|
||||
if len(i.text) < len(secondaryText) {
|
||||
i.autocompleteListSuggestion = secondaryText[len(i.text):]
|
||||
} else if len(mainText) > len(i.text)+1 {
|
||||
i.autocompleteListSuggestion = mainText[len(i.text)+1:]
|
||||
} else if len(i.text) < len(mainText) {
|
||||
i.autocompleteListSuggestion = mainText[len(i.text):]
|
||||
} else {
|
||||
i.autocompleteListSuggestion = ""
|
||||
i.autocompleteListSuggestion = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,10 +616,10 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||
if labelWidth > rightLimit-x {
|
||||
labelWidth = rightLimit - x
|
||||
}
|
||||
Print(screen, []byte(i.label), x, y, labelWidth, AlignLeft, labelColor)
|
||||
Print(screen, i.label, x, y, labelWidth, AlignLeft, labelColor)
|
||||
x += labelWidth
|
||||
} else {
|
||||
_, drawnWidth := Print(screen, []byte(i.label), x, y, rightLimit-x, AlignLeft, labelColor)
|
||||
_, drawnWidth := Print(screen, i.label, x, y, rightLimit-x, AlignLeft, labelColor)
|
||||
x += drawnWidth
|
||||
}
|
||||
|
||||
|
@ -640,26 +640,26 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||
// Text.
|
||||
var cursorScreenPos int
|
||||
text := i.text
|
||||
if text == "" && i.placeholder != "" {
|
||||
if len(text) == 0 && len(i.placeholder) > 0 {
|
||||
// Draw placeholder text.
|
||||
placeholderTextColor := i.placeholderTextColor
|
||||
if i.GetFocusable().HasFocus() && i.placeholderTextColorFocused != ColorUnset {
|
||||
placeholderTextColor = i.placeholderTextColorFocused
|
||||
}
|
||||
Print(screen, []byte(Escape(i.placeholder)), x, y, fieldWidth, AlignLeft, placeholderTextColor)
|
||||
Print(screen, EscapeBytes(i.placeholder), x, y, fieldWidth, AlignLeft, placeholderTextColor)
|
||||
i.offset = 0
|
||||
} else {
|
||||
// Draw entered text.
|
||||
if i.maskCharacter > 0 {
|
||||
text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text))
|
||||
text = bytes.Repeat([]byte(string(i.maskCharacter)), utf8.RuneCount(i.text))
|
||||
}
|
||||
drawnText := ""
|
||||
if fieldWidth >= runewidth.StringWidth(text) {
|
||||
var drawnText []byte
|
||||
if fieldWidth >= runewidth.StringWidth(string(text)) {
|
||||
// We have enough space for the full text.
|
||||
drawnText = Escape(text)
|
||||
Print(screen, []byte(drawnText), x, y, fieldWidth, AlignLeft, fieldTextColor)
|
||||
drawnText = EscapeBytes(text)
|
||||
Print(screen, drawnText, x, y, fieldWidth, AlignLeft, fieldTextColor)
|
||||
i.offset = 0
|
||||
iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
iterateString(string(text), func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
if textPos >= i.cursorPos {
|
||||
return true
|
||||
}
|
||||
|
@ -677,11 +677,11 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||
var shiftLeft int
|
||||
if i.offset > i.cursorPos {
|
||||
i.offset = i.cursorPos
|
||||
} else if subWidth := runewidth.StringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 {
|
||||
} else if subWidth := runewidth.StringWidth(string(text[i.offset:i.cursorPos])); subWidth > fieldWidth-1 {
|
||||
shiftLeft = subWidth - fieldWidth + 1
|
||||
}
|
||||
currentOffset := i.offset
|
||||
iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
iterateString(string(text), func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
if textPos >= currentOffset {
|
||||
if shiftLeft > 0 {
|
||||
i.offset = textPos + textWidth
|
||||
|
@ -695,18 +695,18 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||
}
|
||||
return false
|
||||
})
|
||||
drawnText = Escape(text[i.offset:])
|
||||
Print(screen, []byte(drawnText), x, y, fieldWidth, AlignLeft, fieldTextColor)
|
||||
drawnText = EscapeBytes(text[i.offset:])
|
||||
Print(screen, drawnText, x, y, fieldWidth, AlignLeft, fieldTextColor)
|
||||
}
|
||||
// Draw suggestion
|
||||
if i.maskCharacter == 0 && i.autocompleteListSuggestion != "" {
|
||||
Print(screen, []byte(i.autocompleteListSuggestion), x+runewidth.StringWidth(drawnText), y, fieldWidth-runewidth.StringWidth(drawnText), AlignLeft, i.autocompleteSuggestionTextColor)
|
||||
if i.maskCharacter == 0 && len(i.autocompleteListSuggestion) > 0 {
|
||||
Print(screen, i.autocompleteListSuggestion, x+runewidth.StringWidth(string(drawnText)), y, fieldWidth-runewidth.StringWidth(string(drawnText)), AlignLeft, i.autocompleteSuggestionTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw field note
|
||||
if i.fieldNote != "" {
|
||||
Print(screen, []byte(i.fieldNote), x, y+1, fieldWidth, AlignLeft, i.fieldNoteTextColor)
|
||||
if len(i.fieldNote) > 0 {
|
||||
Print(screen, i.fieldNote, x, y+1, fieldWidth, AlignLeft, i.fieldNoteTextColor)
|
||||
}
|
||||
|
||||
// Draw autocomplete list.
|
||||
|
@ -760,10 +760,10 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
newText := i.text
|
||||
i.Unlock()
|
||||
|
||||
if newText != currentText {
|
||||
if !bytes.Equal(newText, currentText) {
|
||||
i.Autocomplete()
|
||||
if i.changed != nil {
|
||||
i.changed(i.text)
|
||||
i.changed(string(i.text))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -772,29 +772,29 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
home := func() { i.cursorPos = 0 }
|
||||
end := func() { i.cursorPos = len(i.text) }
|
||||
moveLeft := func() {
|
||||
iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
iterateStringReverse(string(i.text[:i.cursorPos]), func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.cursorPos -= textWidth
|
||||
return true
|
||||
})
|
||||
}
|
||||
moveRight := func() {
|
||||
iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
iterateString(string(i.text[i.cursorPos:]), func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.cursorPos += textWidth
|
||||
return true
|
||||
})
|
||||
}
|
||||
moveWordLeft := func() {
|
||||
i.cursorPos = len(regexRightWord.ReplaceAllString(i.text[:i.cursorPos], ""))
|
||||
i.cursorPos = len(regexRightWord.ReplaceAll(i.text[:i.cursorPos], nil))
|
||||
}
|
||||
moveWordRight := func() {
|
||||
i.cursorPos = len(i.text) - len(regexLeftWord.ReplaceAllString(i.text[i.cursorPos:], ""))
|
||||
i.cursorPos = len(i.text) - len(regexLeftWord.ReplaceAll(i.text[i.cursorPos:], nil))
|
||||
}
|
||||
|
||||
// Add character function. Returns whether or not the rune character is
|
||||
// accepted.
|
||||
add := func(r rune) bool {
|
||||
newText := i.text[:i.cursorPos] + string(r) + i.text[i.cursorPos:]
|
||||
if i.accept != nil && !i.accept(newText, r) {
|
||||
newText := append(append(i.text[:i.cursorPos], []byte(string(r))...), i.text[i.cursorPos:]...)
|
||||
if i.accept != nil && !i.accept(string(newText), r) {
|
||||
return false
|
||||
}
|
||||
i.text = newText
|
||||
|
@ -840,17 +840,17 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
}
|
||||
}
|
||||
case tcell.KeyCtrlU: // Delete all.
|
||||
i.text = ""
|
||||
i.text = nil
|
||||
i.cursorPos = 0
|
||||
case tcell.KeyCtrlK: // Delete until the end of the line.
|
||||
i.text = i.text[:i.cursorPos]
|
||||
case tcell.KeyCtrlW: // Delete last word.
|
||||
newText := regexRightWord.ReplaceAllString(i.text[:i.cursorPos], "") + i.text[i.cursorPos:]
|
||||
newText := append(regexRightWord.ReplaceAll(i.text[:i.cursorPos], nil), i.text[i.cursorPos:]...)
|
||||
i.cursorPos -= len(i.text) - len(newText)
|
||||
i.text = newText
|
||||
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete character before the cursor.
|
||||
iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.text = i.text[:textPos] + i.text[textPos+textWidth:]
|
||||
iterateStringReverse(string(i.text[:i.cursorPos]), func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.text = append(i.text[:textPos], i.text[textPos+textWidth:]...)
|
||||
i.cursorPos -= textWidth
|
||||
return true
|
||||
})
|
||||
|
@ -858,8 +858,8 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
i.offset = 0
|
||||
}
|
||||
case tcell.KeyDelete: // Delete character after the cursor.
|
||||
iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.text = i.text[:i.cursorPos] + i.text[i.cursorPos+textWidth:]
|
||||
iterateString(string(i.text[i.cursorPos:]), func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.text = append(i.text[:i.cursorPos], i.text[i.cursorPos+textWidth:]...)
|
||||
return true
|
||||
})
|
||||
case tcell.KeyLeft:
|
||||
|
@ -889,7 +889,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
i.SetText(selectionText)
|
||||
i.Lock()
|
||||
i.autocompleteList = nil
|
||||
i.autocompleteListSuggestion = ""
|
||||
i.autocompleteListSuggestion = nil
|
||||
i.Unlock()
|
||||
} else {
|
||||
i.Unlock()
|
||||
|
@ -899,7 +899,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
case tcell.KeyEscape:
|
||||
if i.autocompleteList != nil {
|
||||
i.autocompleteList = nil
|
||||
i.autocompleteListSuggestion = ""
|
||||
i.autocompleteListSuggestion = nil
|
||||
i.Unlock()
|
||||
} else {
|
||||
i.Unlock()
|
||||
|
@ -952,7 +952,7 @@ func (i *InputField) MouseHandler() func(action MouseAction, event *tcell.EventM
|
|||
if action == MouseLeftClick && y == rectY {
|
||||
// Determine where to place the cursor.
|
||||
if x >= i.fieldX {
|
||||
if !iterateString(i.text, func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool {
|
||||
if !iterateString(string(i.text), func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool {
|
||||
if x-i.fieldX < screenPos+screenWidth {
|
||||
i.cursorPos = textPos
|
||||
return true
|
||||
|
|
6
list.go
6
list.go
|
@ -909,7 +909,7 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// Main text.
|
||||
Print(screen, []byte(item.mainText), x, y, width, AlignLeft, tcell.ColorGray.TrueColor())
|
||||
Print(screen, item.mainText, x, y, width, AlignLeft, tcell.ColorGray.TrueColor())
|
||||
|
||||
RenderScrollBar(screen, l.scrollBarVisibility, scrollBarX, y, scrollBarHeight, len(l.items), scrollBarCursor, index-l.offset, l.hasFocus, l.scrollBarColor)
|
||||
y++
|
||||
|
@ -922,7 +922,7 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// Main text.
|
||||
Print(screen, []byte(item.mainText), x, y, width, AlignLeft, l.mainTextColor)
|
||||
Print(screen, item.mainText, x, y, width, AlignLeft, l.mainTextColor)
|
||||
|
||||
// Background color of selected text.
|
||||
if index == l.currentItem && (!l.selectedFocusOnly || hasFocus) {
|
||||
|
@ -954,7 +954,7 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
|
||||
// Secondary text.
|
||||
if l.showSecondaryText {
|
||||
Print(screen, []byte(item.secondaryText), x, y, width, AlignLeft, l.secondaryTextColor)
|
||||
Print(screen, item.secondaryText, x, y, width, AlignLeft, l.secondaryTextColor)
|
||||
|
||||
RenderScrollBar(screen, l.scrollBarVisibility, scrollBarX, y, scrollBarHeight, len(l.items), scrollBarCursor, index-l.offset, l.hasFocus, l.scrollBarColor)
|
||||
|
||||
|
|
12
util.go
12
util.go
|
@ -253,7 +253,7 @@ func decomposeText(text []byte, findColors, findRegions bool) (colorIndices [][]
|
|||
var from int
|
||||
buf := make([]byte, 0, len(text))
|
||||
for _, indices := range allIndices {
|
||||
buf = append(buf, []byte(text[from:indices[0]])...)
|
||||
buf = append(buf, text[from:indices[0]]...)
|
||||
from = indices[1]
|
||||
}
|
||||
buf = append(buf, text[from:]...)
|
||||
|
@ -566,6 +566,16 @@ func WordWrap(text []byte, width int) (lines [][]byte) {
|
|||
return
|
||||
}
|
||||
|
||||
// EscapeBytes escapes the given text such that color and/or region tags are not
|
||||
// recognized and substituted by the print functions of this package. For
|
||||
// example, to include a tag-like string in a box title or in a TextView:
|
||||
//
|
||||
// box.SetTitle(cview.Escape("[squarebrackets]"))
|
||||
// fmt.Fprint(textView, cview.EscapeBytes(`["quoted"]`))
|
||||
func EscapeBytes(text []byte) []byte {
|
||||
return nonEscapePattern.ReplaceAll(text, []byte("$1[]"))
|
||||
}
|
||||
|
||||
// Escape escapes the given text such that color and/or region tags are not
|
||||
// recognized and substituted by the print functions of this package. For
|
||||
// example, to include a tag-like string in a box title or in a TextView:
|
||||
|
|
Loading…
Reference in New Issue