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 }