skipper/input/input.go

258 lines
4.7 KiB
Go

package input
import "unicode"
import "git.tebibyte.media/sashakoshka/stone"
var selectedInput *Input
func SelectNone () {
selectedInput = nil
}
type Input struct {
cursor int
text []rune
X, Y, Width int
Obscure bool
OnDone func ()
OnCancel func ()
}
func (input *Input) HandleButton (button stone.Button, control, shift, alt bool) {
switch button {
case stone.KeyLeft:
if control {
input.cursor -= input.distanceToPreviousWordBound()
} else if alt {
input.cursorToBeginning()
} else {
input.cursor --
}
input.constrainCursor()
case stone.KeyRight:
if control {
input.cursor += input.distanceToNextWordBound()
} else if alt {
input.cursorToEnd()
} else {
input.cursor ++
}
input.constrainCursor()
case stone.KeyHome:
input.cursorToBeginning()
input.constrainCursor()
case stone.KeyEnd:
input.cursorToEnd()
input.constrainCursor()
case stone.KeyBackspace:
if control {
input.backspace(input.distanceToPreviousWordBound())
} else if alt {
input.backspace(input.cursor)
} else {
input.backspace(1)
}
input.constrainCursor()
case stone.KeyDelete:
if control {
input.delete(input.distanceToNextWordBound())
} else if alt {
input.delete(len(input.text))
} else {
input.delete(1)
}
input.constrainCursor()
case stone.KeyEnter:
SelectNone()
if input.OnDone != nil {
input.OnDone()
}
case stone.KeyEscape:
SelectNone()
if input.OnCancel != nil {
input.OnCancel()
}
default:
if button.Printable() {
input.text = append(input.text, 0)
copy (
input.text[input.cursor + 1:],
input.text[input.cursor:])
input.text[input.cursor] = rune(button)
input.cursor ++
}
}
return
}
func (input *Input) Draw (buffer stone.Buffer) {
buffer.SetRune(input.X, input.Y, '[')
buffer.SetRune(input.X + 1, input.Y, 0)
buffer.SetRune(input.X + input.Width - 2, input.Y, 0)
buffer.SetRune(input.X + input.Width - 1, input.Y, ']')
actualWidth := input.Width - 4
scroll := 0
x := input.X + 2
for index := 0; index < actualWidth; index ++ {
character := input.charAtSafe(index + scroll)
if input.Selected() && index == input.cursor {
buffer.SetStyle(x, input.Y, stone.StyleUnderline)
} else {
buffer.SetStyle(x, input.Y, stone.StyleNormal)
}
if input.Obscure && character > 0 {
buffer.SetRune(x, input.Y, '*')
} else {
buffer.SetRune(x, input.Y, character)
}
x ++
}
}
func (input *Input) charAtSafe (index int) (character rune) {
if index < 0 { return }
if index >= len(input.text) { return }
character = input.text[index]
return
}
func (input *Input) constrainCursor () (constrained bool) {
if input.cursor < 0 {
input.cursorToBeginning()
constrained = true
} else if input.cursor > len(input.text) {
input.cursorToEnd()
constrained = true
}
return
}
func (input *Input) distanceToPreviousWordBound () (distance int) {
index := input.cursor
index --
distance ++
for {
if index < 0 { break }
character := input.text[index]
isWordChar :=
unicode.IsLetter(character) ||
unicode.IsDigit(character)
if !isWordChar {
distance --
break
}
distance ++
index --
}
if distance < 1 {
distance ++
}
return
}
func (input *Input) distanceToNextWordBound () (distance int) {
index := input.cursor
for {
if index >= len(input.text) { break }
character := input.text[index]
isWordChar :=
unicode.IsLetter(character) ||
unicode.IsDigit(character)
if !isWordChar {
break
}
distance ++
index ++
}
if distance < 1 {
distance ++
}
return
}
func (input *Input) backspace (amount int) {
if input.cursor - amount < 0 {
amount = input.cursor
}
copy (
input.text[input.cursor - amount:],
input.text[input.cursor:])
input.text = input.text[:len(input.text) - amount]
input.cursor -= amount
}
func (input *Input) delete (amount int) {
if input.cursor + amount > len(input.text) {
amount = len(input.text) - input.cursor
}
// TODO
copy (
input.text[input.cursor:],
input.text[input.cursor + amount:])
input.text = input.text[:len(input.text) - amount]
}
func (input *Input) cursorToBeginning () {
input.cursor = 0
}
func (input *Input) cursorToEnd () {
input.cursor = len(input.text)
}
func (input *Input) Reset () {
input.SetText("")
}
func (input *Input) Text () (text string) {
text = string(input.text)
return
}
func (input *Input) SetText (text string) {
input.text = []rune(text)
input.cursorToEnd()
return
}
func (input *Input) Selected () (selected bool) {
selected = selectedInput == input
return
}
func (input *Input) Select () () {
selectedInput = input
return
}
func Selected () (selected *Input) {
selected = selectedInput
return
}