clui/edit.go

280 lines
6.1 KiB
Go
Raw Normal View History

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"
"strings"
2015-09-21 20:54:39 -07:00
)
2015-10-28 14:17:49 -07:00
// OnChange sets the callback that is called when EditField content is changed
2015-09-21 20:54:39 -07:00
func (e *EditField) OnChange(fn func(Event)) {
e.onChange = fn
}
// OnKeyPress sets the callback that is called when a user presses a Key while
// the controls is active. If a handler processes the key it should return
// true. If handler returns false it means that the default handler will
// process the key
func (e *EditField) OnKeyPress(fn func(term.Key, rune) bool) {
e.onKeyPress = fn
}
2015-10-28 14:17:49 -07:00
// SetTitle changes the EditField content and emits OnChage eventif the new value does not equal to old one
2015-10-16 10:27:43 -07:00
func (e *EditField) SetTitle(title string) {
2018-03-15 20:56:29 -07:00
e.setTitleInternal(title)
e.end()
}
func (e *EditField) setTitleInternal(title string) {
2015-09-21 20:54:39 -07:00
if e.title != title {
e.title = title
2018-03-15 20:56:29 -07:00
2015-09-21 20:54:39 -07:00
if e.onChange != nil {
ev := Event{Msg: title}
e.onChange(ev)
2015-09-21 20:54:39 -07:00
}
}
if title == "" {
e.cursorPos = xs.Len(title)
}
2015-09-21 20:54:39 -07:00
}
2015-10-28 14:17:49 -07:00
// Repaint draws the control on its View surface
func (e *EditField) Draw() {
2018-03-13 22:14:40 -07:00
if e.hidden {
return
}
PushAttributes()
defer PopAttributes()
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
parts := []rune(SysObject(ObjEdit))
2015-10-16 10:27:43 -07:00
chLeft, chRight := string(parts[0]), string(parts[1])
chStar := "*"
if len(parts) > 3 {
chStar = string(parts[3])
}
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 {
if e.showStars {
textOut = strings.Repeat(chStar, xs.Len(e.title))
} else {
textOut = e.title
}
2015-09-21 20:54:39 -07:00
} else {
fromIdx := 0
toIdx := 0
if e.offset == 0 {
2015-10-16 10:27:43 -07:00
toIdx = e.width - 1
if e.showStars {
textOut = strings.Repeat(chStar, toIdx) + chRight
} else {
textOut = xs.Slice(e.title, 0, toIdx) + chRight
}
2015-09-21 20:54:39 -07:00
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
if e.showStars {
textOut = chLeft + strings.Repeat(chStar, toIdx-fromIdx) + chRight
} else {
textOut = chLeft + xs.Slice(e.title, fromIdx, toIdx) + chRight
}
2015-09-21 20:54:39 -07:00
} else {
if e.showStars {
textOut = chLeft + strings.Repeat(chStar, xs.Len(e.title)-fromIdx)
} else {
textOut = chLeft + xs.Slice(e.title, fromIdx, -1)
}
2015-09-21 20:54:39 -07:00
}
}
}
fg, bg := RealColor(e.fg, ColorEditText), RealColor(e.bg, ColorEditBack)
2015-10-19 12:05:43 -07:00
if !e.Enabled() {
fg, bg = RealColor(e.fg, ColorDisabledText), RealColor(e.fg, ColorDisabledBack)
2015-10-19 12:05:43 -07:00
} else if e.Active() {
fg, bg = RealColor(e.fg, ColorEditActiveText), RealColor(e.bg, ColorEditActiveBack)
2015-10-19 12:05:43 -07:00
}
2015-09-21 20:54:39 -07:00
SetTextColor(fg)
SetBackColor(bg)
FillRect(x, y, w, 1, ' ')
DrawRawText(x, y, textOut)
if e.Active() {
SetCursorPos(e.cursorPos+e.x+curOff, 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 {
2018-03-15 20:56:29 -07:00
e.setTitleInternal(string(ch) + e.title)
2015-09-21 20:54:39 -07:00
} else if idx >= xs.Len(e.title) {
2018-03-15 20:56:29 -07:00
e.setTitleInternal(e.title + string(ch))
2015-09-21 20:54:39 -07:00
} else {
2018-03-15 20:56:29 -07:00
e.setTitleInternal(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--
2018-03-15 20:56:29 -07:00
e.setTitleInternal(xs.Slice(e.title, 0, length-1))
2015-09-21 20:54:39 -07:00
} else if e.cursorPos == 1 {
e.cursorPos = 0
2018-03-15 20:56:29 -07:00
e.setTitleInternal(xs.Slice(e.title, 1, -1))
2015-09-21 20:54:39 -07:00
e.offset = 0
} else {
e.cursorPos--
2018-03-15 20:56:29 -07:00
e.setTitleInternal(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 {
2018-03-15 20:56:29 -07:00
e.setTitleInternal(xs.Slice(e.title, 0, length-1))
2015-09-21 20:54:39 -07:00
} else {
2018-03-15 20:56:29 -07:00
e.setTitleInternal(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)
}
2015-10-28 14:17:49 -07:00
// Clear empties the EditField and emits OnChange event
2015-09-21 20:54:39 -07:00
func (e *EditField) Clear() {
e.home()
2018-03-15 20:56:29 -07:00
e.setTitleInternal("")
2015-09-21 20:54:39 -07:00
}
2015-10-28 14:17:49 -07:00
// SetMaxWidth sets the maximum lenght of the EditField text. If the current text is longer it is truncated
2015-09-21 20:54:39 -07:00
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()
}
}
2015-10-28 14:17:49 -07:00
// MaxWidth returns the current maximum text length. Zero means no limit
func (e *EditField) MaxWidth() int {
2015-09-21 20:54:39 -07:00
return e.maxWidth
}
// SetSize changes control size. Constant DoNotChange can be
// used as placeholder to indicate that the control attrubute
// should be unchanged.
// Method does nothing if new size is less than minimal size
// EditField height cannot be changed - it equals 1 always
func (e *EditField) SetSize(width, height int) {
if width != KeepValue && (width > 1000 || width < e.minW) {
return
}
if height != KeepValue && (height > 200 || height < e.minH) {
return
}
if width != KeepValue {
e.width = width
}
e.height = 1
}
// PasswordMode returns whether password mode is enabled for the control
func (e *EditField) PasswordMode() bool {
return e.showStars
}
// SetPasswordMode changes the way an EditField displays it content.
// If PasswordMode is false then the EditField works as regular text entry
// control. If PasswordMode is true then the EditField shows its content hidden
// with star characters ('*' by default)
func (e *EditField) SetPasswordMode(pass bool) {
e.showStars = pass
}