2015-09-21 20:54:39 -07:00
|
|
|
package clui
|
|
|
|
|
|
|
|
import (
|
|
|
|
xs "github.com/huandu/xstrings"
|
2015-10-16 10:27:43 -07:00
|
|
|
term "github.com/nsf/termbox-go"
|
2015-09-21 20:54:39 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
Text edit field contol. Can be simple edit field(default mode) and edit field
|
|
|
|
with drop down list. The EditField mode is set during creation and cannot be changed on the fly.
|
|
|
|
Edit field consumes some keyboard events when it is active: all printable charaters;
|
|
|
|
Delete, BackSpace, Home, End, left and right arrows; Enter, up and down arrows if EditField
|
|
|
|
in combobox mode and drop down list is visible; F5 to open drop down list in combobox mode;
|
|
|
|
Ctrl+R to clear EditField.
|
|
|
|
Edit text can be limited. By default a user can enter text of any lenght. Use SetMaxWidth to limit the maximum text length. If the text is longer than maximun then the text is automatically truncated.
|
|
|
|
EditField call funtion onChage in case of its text is changed. Event field Msg contains the new text
|
|
|
|
*/
|
|
|
|
type EditField struct {
|
2015-10-16 10:27:43 -07:00
|
|
|
ControlBase
|
2015-09-21 20:54:39 -07:00
|
|
|
// cursor position in edit text
|
|
|
|
cursorPos int
|
|
|
|
// the number of the first displayed text character - it is used in case of text is longer than edit width
|
2015-10-16 10:27:43 -07:00
|
|
|
offset int
|
|
|
|
readonly bool
|
|
|
|
maxWidth int
|
2015-09-21 20:54:39 -07:00
|
|
|
|
|
|
|
onChange func(Event)
|
|
|
|
}
|
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
func NewEditField(view View, parent Control, width int, text string, scale int) *EditField {
|
2015-09-21 20:54:39 -07:00
|
|
|
e := new(EditField)
|
|
|
|
e.onChange = nil
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(text)
|
2015-09-21 20:54:39 -07:00
|
|
|
e.SetEnabled(true)
|
2015-10-20 09:43:56 -07:00
|
|
|
|
|
|
|
if width == AutoSize {
|
|
|
|
width = xs.Len(text) + 1
|
|
|
|
}
|
|
|
|
|
2015-09-21 20:54:39 -07:00
|
|
|
e.SetSize(width, 1)
|
|
|
|
e.cursorPos = xs.Len(text)
|
|
|
|
e.offset = 0
|
|
|
|
e.parent = parent
|
2015-10-16 10:27:43 -07:00
|
|
|
e.view = view
|
|
|
|
e.parent = parent
|
|
|
|
e.readonly = false
|
2015-09-21 20:54:39 -07:00
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetConstraints(width, 1)
|
2015-09-21 20:54:39 -07:00
|
|
|
|
|
|
|
e.end()
|
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
if parent != nil {
|
|
|
|
parent.AddChild(e, scale)
|
|
|
|
}
|
|
|
|
|
2015-09-21 20:54:39 -07:00
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) OnChange(fn func(Event)) {
|
|
|
|
e.onChange = fn
|
|
|
|
}
|
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
func (e *EditField) SetTitle(title string) {
|
2015-09-21 20:54:39 -07:00
|
|
|
if e.title != title {
|
|
|
|
e.title = title
|
|
|
|
if e.onChange != nil {
|
2015-10-16 10:27:43 -07:00
|
|
|
ev := Event{Msg: title, Sender: e}
|
2015-09-21 20:54:39 -07:00
|
|
|
go e.onChange(ev)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
func (e *EditField) Repaint() {
|
|
|
|
canvas := e.view.Canvas()
|
2015-09-21 20:54:39 -07:00
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
x, y := e.Pos()
|
|
|
|
w, _ := e.Size()
|
2015-09-21 20:54:39 -07:00
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
tm := e.view.Screen().Theme()
|
|
|
|
parts := []rune(tm.SysObject(ObjEdit))
|
|
|
|
chLeft, chRight := string(parts[0]), string(parts[1])
|
2015-09-21 20:54:39 -07:00
|
|
|
|
|
|
|
var textOut string
|
|
|
|
curOff := 0
|
2015-10-16 10:27:43 -07:00
|
|
|
if e.offset == 0 && xs.Len(e.title) < e.width {
|
2015-09-21 20:54:39 -07:00
|
|
|
textOut = e.title
|
|
|
|
} else {
|
|
|
|
fromIdx := 0
|
|
|
|
toIdx := 0
|
|
|
|
if e.offset == 0 {
|
2015-10-16 10:27:43 -07:00
|
|
|
toIdx = e.width - 1
|
2015-09-21 20:54:39 -07:00
|
|
|
textOut = xs.Slice(e.title, 0, toIdx) + chRight
|
|
|
|
curOff = -e.offset
|
|
|
|
} else {
|
|
|
|
curOff = 1 - e.offset
|
|
|
|
fromIdx = e.offset
|
2015-10-16 10:27:43 -07:00
|
|
|
if e.width-1 <= xs.Len(e.title)-e.offset {
|
2015-09-21 20:54:39 -07:00
|
|
|
toIdx = e.offset + e.width - 2
|
|
|
|
textOut = chLeft + xs.Slice(e.title, fromIdx, toIdx) + chRight
|
|
|
|
} else {
|
|
|
|
textOut = chLeft + xs.Slice(e.title, fromIdx, -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
fg, bg := RealColor(tm, e.fg, ColorEditText), RealColor(tm, e.bg, ColorEditBack)
|
2015-10-19 12:05:43 -07:00
|
|
|
if !e.Enabled() {
|
|
|
|
fg, bg = RealColor(tm, e.fg, ColorDisabledText), RealColor(tm, e.fg, ColorDisabledBack)
|
|
|
|
} else if e.Active() {
|
|
|
|
fg, bg = RealColor(tm, e.fg, ColorEditActiveText), RealColor(tm, e.bg, ColorEditActiveBack)
|
|
|
|
|
|
|
|
}
|
2015-09-21 20:54:39 -07:00
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
canvas.FillRect(x, y, w, 1, term.Cell{Ch: ' ', Bg: bg})
|
|
|
|
canvas.PutText(x, y, textOut, fg, bg)
|
2015-09-21 20:54:39 -07:00
|
|
|
if e.active {
|
2015-10-16 10:27:43 -07:00
|
|
|
wx, wy := e.view.Pos()
|
|
|
|
canvas.SetCursorPos(e.cursorPos+curOff+wx+e.x, wy+e.y)
|
2015-09-21 20:54:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) insertRune(ch rune) {
|
|
|
|
if e.readonly {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.maxWidth > 0 && xs.Len(e.title) >= e.maxWidth {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
idx := e.cursorPos
|
|
|
|
|
|
|
|
if idx == 0 {
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(string(ch) + e.title)
|
2015-09-21 20:54:39 -07:00
|
|
|
} else if idx >= xs.Len(e.title) {
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(e.title + string(ch))
|
2015-09-21 20:54:39 -07:00
|
|
|
} else {
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(xs.Slice(e.title, 0, idx) + string(ch) + xs.Slice(e.title, idx, -1))
|
2015-09-21 20:54:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
e.cursorPos++
|
|
|
|
|
|
|
|
if e.cursorPos >= e.width {
|
|
|
|
if e.offset == 0 {
|
|
|
|
e.offset = 2
|
|
|
|
} else {
|
|
|
|
e.offset++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) backspace() {
|
|
|
|
if e.title == "" || e.cursorPos == 0 || e.readonly {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
length := xs.Len(e.title)
|
|
|
|
if e.cursorPos >= length {
|
|
|
|
e.cursorPos--
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(xs.Slice(e.title, 0, length-1))
|
2015-09-21 20:54:39 -07:00
|
|
|
} else if e.cursorPos == 1 {
|
|
|
|
e.cursorPos = 0
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(xs.Slice(e.title, 1, -1))
|
2015-09-21 20:54:39 -07:00
|
|
|
e.offset = 0
|
|
|
|
} else {
|
|
|
|
e.cursorPos--
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
|
2015-09-21 20:54:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if length-1 < e.width {
|
|
|
|
e.offset = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) del() {
|
|
|
|
length := xs.Len(e.title)
|
|
|
|
|
|
|
|
if e.title == "" || e.cursorPos == length || e.readonly {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.cursorPos == length-1 {
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(xs.Slice(e.title, 0, length-1))
|
2015-09-21 20:54:39 -07:00
|
|
|
} else {
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
|
2015-09-21 20:54:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if length-1 < e.width {
|
|
|
|
e.offset = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) charLeft() {
|
|
|
|
if e.cursorPos == 0 || e.title == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.cursorPos == e.offset {
|
|
|
|
e.offset--
|
|
|
|
}
|
|
|
|
|
|
|
|
e.cursorPos--
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) charRight() {
|
|
|
|
length := xs.Len(e.title)
|
|
|
|
if e.cursorPos == length || e.title == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
e.cursorPos++
|
|
|
|
if e.cursorPos != length && e.cursorPos >= e.offset+e.width-2 {
|
|
|
|
e.offset++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) home() {
|
|
|
|
e.offset = 0
|
|
|
|
e.cursorPos = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) end() {
|
|
|
|
length := xs.Len(e.title)
|
|
|
|
e.cursorPos = length
|
|
|
|
|
|
|
|
if length < e.width {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
e.offset = length - (e.width - 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) Clear() {
|
|
|
|
e.home()
|
2015-10-16 10:27:43 -07:00
|
|
|
e.SetTitle("")
|
2015-09-21 20:54:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) ProcessEvent(event Event) bool {
|
2015-10-16 10:27:43 -07:00
|
|
|
if !e.Active() || !e.Enabled() {
|
2015-09-21 20:54:39 -07:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if event.Type == EventActivate && event.X == 0 {
|
2015-10-16 10:27:43 -07:00
|
|
|
term.HideCursor()
|
2015-09-21 20:54:39 -07:00
|
|
|
}
|
|
|
|
|
2015-10-16 10:27:43 -07:00
|
|
|
if event.Type == EventKey && event.Key != term.KeyTab && event.Key != term.KeyEnter {
|
2015-09-21 20:54:39 -07:00
|
|
|
switch event.Key {
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeySpace:
|
2015-09-21 20:54:39 -07:00
|
|
|
e.insertRune(' ')
|
|
|
|
return true
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeyBackspace:
|
2015-09-21 20:54:39 -07:00
|
|
|
e.backspace()
|
|
|
|
return true
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeyDelete:
|
2015-09-21 20:54:39 -07:00
|
|
|
e.del()
|
|
|
|
return true
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeyArrowLeft:
|
2015-09-21 20:54:39 -07:00
|
|
|
e.charLeft()
|
|
|
|
return true
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeyHome:
|
2015-09-21 20:54:39 -07:00
|
|
|
e.home()
|
|
|
|
return true
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeyEnd:
|
2015-09-21 20:54:39 -07:00
|
|
|
e.end()
|
|
|
|
return true
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeyCtrlR:
|
2015-09-21 20:54:39 -07:00
|
|
|
if !e.readonly {
|
|
|
|
e.Clear()
|
|
|
|
}
|
|
|
|
return true
|
2015-10-16 10:27:43 -07:00
|
|
|
case term.KeyArrowRight:
|
2015-09-21 20:54:39 -07:00
|
|
|
e.charRight()
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
if event.Ch != 0 {
|
|
|
|
e.insertRune(event.Ch)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) SetMaxWidth(w int) {
|
|
|
|
e.maxWidth = w
|
|
|
|
if w > 0 && xs.Len(e.title) > w {
|
|
|
|
e.title = xs.Slice(e.title, 0, w)
|
|
|
|
e.end()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *EditField) GetMaxWidth() int {
|
|
|
|
return e.maxWidth
|
|
|
|
}
|