2015-10-21 16:51:22 +00:00
|
|
|
package termboxUtil
|
2015-05-02 03:15:36 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2016-01-05 00:25:07 +00:00
|
|
|
"strings"
|
2015-10-21 16:51:22 +00:00
|
|
|
|
2015-05-02 03:15:36 +00:00
|
|
|
"github.com/nsf/termbox-go"
|
|
|
|
)
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// InputField is a field for inputting text
|
2015-05-02 03:15:36 +00:00
|
|
|
type InputField struct {
|
|
|
|
value string
|
|
|
|
x, y, width, height int
|
|
|
|
cursor int
|
|
|
|
fg, bg termbox.Attribute
|
|
|
|
bordered bool
|
2016-01-01 18:15:09 +00:00
|
|
|
wrap bool
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// CreateInputField creates an input field at x, y that is w by h
|
2015-05-02 03:15:36 +00:00
|
|
|
func CreateInputField(x, y, w, h int, fg, bg termbox.Attribute) *InputField {
|
|
|
|
i := InputField{x: x, y: y, width: w, height: h, fg: fg, bg: bg}
|
|
|
|
return &i
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// GetValue gets the current text that is in the InputField
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) GetValue() string { return i.value }
|
2015-10-21 16:51:22 +00:00
|
|
|
|
|
|
|
// SetValue sets the current text in the InputField to s
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) SetValue(s string) *InputField {
|
|
|
|
i.value = s
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// GetX returns the x position of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) GetX() int { return i.x }
|
2015-10-21 16:51:22 +00:00
|
|
|
|
|
|
|
// SetX sets the x position of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) SetX(x int) *InputField {
|
|
|
|
i.x = x
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// GetY returns the y position of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) GetY() int { return i.y }
|
2015-10-21 16:51:22 +00:00
|
|
|
|
|
|
|
// SetY sets the y position of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) SetY(y int) *InputField {
|
|
|
|
i.y = y
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// GetWidth returns the current width of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) GetWidth() int { return i.width }
|
2015-10-21 16:51:22 +00:00
|
|
|
|
|
|
|
// SetWidth sets the current width of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) SetWidth(w int) *InputField {
|
|
|
|
i.width = w
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// GetHeight returns the current height of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) GetHeight() int { return i.height }
|
2015-10-21 16:51:22 +00:00
|
|
|
|
|
|
|
// SetHeight sets the current height of the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) SetHeight(h int) *InputField {
|
|
|
|
i.height = h
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// IsBordered returns true or false if this input field has a border
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) IsBordered() bool { return i.bordered }
|
2015-10-21 16:51:22 +00:00
|
|
|
|
|
|
|
// SetBordered sets whether we render a border around the input field
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) SetBordered(b bool) *InputField {
|
|
|
|
i.bordered = b
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2016-01-01 18:15:09 +00:00
|
|
|
// DoesWrap returns true or false if this input field wraps text
|
|
|
|
func (i *InputField) DoesWrap() bool { return i.wrap }
|
|
|
|
|
|
|
|
// SetWrap sets whether we wrap the text at width.
|
|
|
|
// If 'wrap' is set, we automatically increase the height when we need to.
|
|
|
|
func (i *InputField) SetWrap(b bool) *InputField {
|
|
|
|
i.wrap = b
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// HandleKeyPress accepts the termbox event and returns whether it was consumed
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) HandleKeyPress(event termbox.Event) bool {
|
2016-01-05 00:25:07 +00:00
|
|
|
if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
|
2015-05-02 03:15:36 +00:00
|
|
|
if len(i.value) > 0 {
|
|
|
|
i.value = i.value[:len(i.value)-1]
|
|
|
|
}
|
|
|
|
} else if event.Key == termbox.KeyArrowLeft {
|
|
|
|
if i.cursor+len(i.value) > 0 {
|
2015-10-21 16:51:22 +00:00
|
|
|
i.cursor--
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|
|
|
|
} else if event.Key == termbox.KeyArrowRight {
|
|
|
|
if i.cursor < 0 {
|
2015-10-21 16:51:22 +00:00
|
|
|
i.cursor++
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|
2015-05-04 18:52:10 +00:00
|
|
|
} else if event.Key == termbox.KeyCtrlU {
|
2016-01-05 00:25:07 +00:00
|
|
|
// Ctrl+U Clears the Input (before the cursor)
|
|
|
|
i.value = i.value[i.cursor:]
|
2015-05-02 03:15:36 +00:00
|
|
|
} else {
|
2015-10-21 16:51:22 +00:00
|
|
|
// Get the rune to add to our value. Space and Tab are special cases where
|
|
|
|
// we can't use the event's rune directly
|
|
|
|
var ch string
|
|
|
|
switch event.Key {
|
|
|
|
case termbox.KeySpace:
|
|
|
|
ch = " "
|
|
|
|
case termbox.KeyTab:
|
|
|
|
ch = "\t"
|
2016-01-05 00:25:07 +00:00
|
|
|
case termbox.KeyEnter:
|
|
|
|
ch = "\n"
|
2015-10-21 16:51:22 +00:00
|
|
|
default:
|
|
|
|
ch = string(event.Ch)
|
|
|
|
}
|
|
|
|
|
2015-05-02 03:15:36 +00:00
|
|
|
if i.cursor+len(i.value) == 0 {
|
2015-10-21 16:51:22 +00:00
|
|
|
i.value = fmt.Sprintf("%s%s", ch, i.value)
|
2015-05-02 03:15:36 +00:00
|
|
|
} else if i.cursor == 0 {
|
2015-10-21 16:51:22 +00:00
|
|
|
i.value = fmt.Sprintf("%s%s", i.value, ch)
|
2015-05-02 03:15:36 +00:00
|
|
|
} else {
|
2015-10-21 16:51:22 +00:00
|
|
|
strPt1 := i.value[:(len(i.value) + i.cursor)]
|
|
|
|
strPt2 := i.value[(len(i.value) + i.cursor):]
|
|
|
|
i.value = fmt.Sprintf("%s%s%s", strPt1, ch, strPt2)
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
// Draw outputs the input field on the screen
|
2015-05-02 03:15:36 +00:00
|
|
|
func (i *InputField) Draw() {
|
2016-01-11 02:42:51 +00:00
|
|
|
maxWidth := i.width
|
|
|
|
x, y := i.x, i.y
|
|
|
|
startX := i.x
|
2015-05-02 03:15:36 +00:00
|
|
|
if i.bordered {
|
|
|
|
DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
|
2016-01-11 02:42:51 +00:00
|
|
|
maxWidth--
|
|
|
|
x++
|
|
|
|
y++
|
|
|
|
startX++
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|
|
|
|
|
2015-10-21 16:51:22 +00:00
|
|
|
var strPt1, strPt2 string
|
|
|
|
var cursorRune rune
|
2015-05-02 03:15:36 +00:00
|
|
|
if len(i.value) > 0 {
|
|
|
|
if i.cursor+len(i.value) == 0 {
|
2015-10-21 16:51:22 +00:00
|
|
|
strPt1 = ""
|
|
|
|
strPt2 = i.value[1:]
|
|
|
|
cursorRune = rune(i.value[0])
|
2015-05-02 03:15:36 +00:00
|
|
|
} else if i.cursor == 0 {
|
2015-10-21 16:51:22 +00:00
|
|
|
strPt1 = i.value
|
|
|
|
strPt2 = ""
|
|
|
|
cursorRune = ' '
|
2015-05-02 03:15:36 +00:00
|
|
|
} else {
|
2015-10-21 16:51:22 +00:00
|
|
|
strPt1 = i.value[:(len(i.value) + i.cursor)]
|
|
|
|
strPt2 = i.value[(len(i.value)+i.cursor)+1:]
|
|
|
|
cursorRune = rune(i.value[len(i.value)+i.cursor])
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|
|
|
|
} else {
|
2015-10-21 16:51:22 +00:00
|
|
|
strPt1, strPt2, cursorRune = "", "", ' '
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|
2016-01-04 04:14:33 +00:00
|
|
|
// strPt1, strPt2 = all of the text before, after the cursor
|
|
|
|
// cursorRune is the rune on the cursor
|
2016-01-04 17:01:22 +00:00
|
|
|
if i.wrap {
|
|
|
|
// Split the text into maxWidth chunks
|
2016-01-11 02:42:51 +00:00
|
|
|
for len(strPt1) > maxWidth { //|| nlCount > 0 {
|
2016-01-05 00:25:07 +00:00
|
|
|
breakAt := maxWidth
|
2016-01-11 02:42:51 +00:00
|
|
|
DrawStringAtPoint(strPt1[:breakAt], x, y, i.fg, i.bg)
|
|
|
|
x = startX
|
|
|
|
y++
|
2016-01-05 00:25:07 +00:00
|
|
|
strPt1 = strPt1[breakAt:]
|
2016-01-04 17:01:22 +00:00
|
|
|
}
|
2016-01-05 00:25:07 +00:00
|
|
|
x, y = DrawStringAtPoint(strPt1, x, y, i.fg, i.bg)
|
2016-01-04 17:01:22 +00:00
|
|
|
if maxWidth-len(strPt1) <= 0 {
|
|
|
|
termbox.SetCell(x, y, cursorRune, i.bg, i.fg)
|
|
|
|
}
|
2016-01-05 00:25:07 +00:00
|
|
|
if len(strPt2) > 0 {
|
|
|
|
if maxWidth-len(strPt1)-1 > 0 {
|
|
|
|
DrawStringAtPoint(strPt2[:(maxWidth-len(strPt1)-1)], x+1, y, i.fg, i.bg)
|
|
|
|
strPt2 = strPt2[(maxWidth - len(strPt1)):]
|
|
|
|
}
|
|
|
|
nlCount := strings.Count(strPt2, "\n")
|
|
|
|
for len(strPt2) > maxWidth || nlCount > 0 {
|
2016-01-04 17:01:22 +00:00
|
|
|
x, y = DrawStringAtPoint(strPt2[:maxWidth], x, y, i.fg, i.bg)
|
2016-01-05 00:25:07 +00:00
|
|
|
strPt2 = strPt2[maxWidth:]
|
2016-01-04 17:01:22 +00:00
|
|
|
}
|
|
|
|
x, y = DrawStringAtPoint(strPt2, x, y, i.fg, i.bg)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Not wrapping, just adjust the viewport
|
2016-01-04 04:14:33 +00:00
|
|
|
for len(strPt1)+len(strPt2)+1 > maxWidth {
|
2016-01-04 17:01:22 +00:00
|
|
|
if len(strPt1) >= len(strPt2) {
|
|
|
|
if len(strPt1) == 0 {
|
|
|
|
break
|
|
|
|
}
|
2016-01-04 04:14:33 +00:00
|
|
|
strPt1 = strPt1[1:]
|
2016-01-04 17:01:22 +00:00
|
|
|
} else {
|
2016-01-04 04:14:33 +00:00
|
|
|
strPt2 = strPt2[:len(strPt2)-1]
|
2016-01-01 18:15:09 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 17:01:22 +00:00
|
|
|
x, y := DrawStringAtPoint(strPt1, i.x+1, i.y+1, i.fg, i.bg)
|
|
|
|
termbox.SetCell(x, y, cursorRune, i.bg, i.fg)
|
|
|
|
DrawStringAtPoint(strPt2, x+1, y, i.fg, i.bg)
|
2016-01-01 18:15:09 +00:00
|
|
|
}
|
2015-05-02 03:15:36 +00:00
|
|
|
}
|