diff options
Diffstat (limited to 'tui/text_input.go')
| -rw-r--r-- | tui/text_input.go | 140 |
1 files changed, 134 insertions, 6 deletions
diff --git a/tui/text_input.go b/tui/text_input.go index a8c7d6d..3bceb24 100644 --- a/tui/text_input.go +++ b/tui/text_input.go @@ -1,15 +1,143 @@ package tui import ( -// "github.com/rivo/uniseg" -// "strings" + "github.com/rivo/uniseg" + "zgo.at/termfo/keys" + "strings" ) type TextInput struct { - Contents string - Cursor int + id string + linesBefore []string + beforeCursor string + selection string + selectionBefore bool + afterCursor string + linesAfter []string } -func (t *TextInput) Update(ev Event) { - t.Cursor = max(t.Cursor, 0) +func (t *TextInput) Text() string { + return t.beforeCursor + t.selection + t.afterCursor +} + +func (t *TextInput) SetText(text string) { + t.beforeCursor = "" + t.afterCursor = text +} + +func toGraphemes(s string) []string { + g := uniseg.NewGraphemes(s) + var result []string + for g.Next() { + result = append(result, g.Str()) + } + return result +} + +func splitRight(s string) (string, string) { + if s == "" { + return "", "" + } + gs := toGraphemes(s) + return strings.Join(gs[:len(gs) - 1], ""), gs[len(gs) - 1] +} + +func splitLeft(s string) (string, string) { + if s == "" { + return "", "" + } + cluster, rest, _, _ := uniseg.FirstGraphemeCluster([]byte(s), 0) + return string(cluster), string(rest) +} + +func (t *TextInput) Left(selection bool, word bool) { + t.Deselect() + var right string + t.beforeCursor, right = splitRight(t.beforeCursor) + t.afterCursor = right + t.afterCursor +} + +func (t *TextInput) Right(selection bool, word bool) { + t.Deselect() + var left string + left, t.afterCursor = splitLeft(t.afterCursor) + t.beforeCursor += left +} + +func (t *TextInput) Start(selection bool) { + t.afterCursor = t.beforeCursor + t.afterCursor + t.beforeCursor = "" +} + +func (t *TextInput) End(selection bool) { + t.beforeCursor = t.beforeCursor + t.afterCursor + t.afterCursor = "" +} + +func (t *TextInput) Selection() string { + return t.selection +} + +func (t *TextInput) Deselect() { + if t.selectionBefore { + t.beforeCursor += t.selection + } else { + t.afterCursor = t.selection + t.afterCursor + } + t.selection = "" +} + +func (t *TextInput) Write(text string) { + t.selection = "" + t.beforeCursor += text +} + +func (t *TextInput) Update(ev Event) (usedKeybind bool) { + if Selected != t.id { + return + } + if ev.TextInput != 0 { + t.Write(string(ev.TextInput)) + } + + selection := ev.Key & keys.Shift != 0 + word := ev.Key & keys.Ctrl != 0 + switch ev.Key.WithoutMods() { + case keys.Left: + t.Left(selection, word) + case keys.Right: + t.Right(selection, word) + case 'a': + if ev.Key & keys.Ctrl != 0 { + t.Start(selection) + } + case 'e': + if ev.Key & keys.Ctrl != 0 { + t.End(selection) + } + case keys.Backspace: + if t.selection != "" { + t.selection = "" + } else { + t.beforeCursor, _ = splitRight(t.beforeCursor) + } + default: + return false + } + return true +} + +func (t *TextInput) Show(id string) { + t.id = id + Push(id, Box {Width: Fill, Height: 4})//TextSize}) + Text(t.beforeCursor, nil) + if t.selectionBefore { + Text(t.selection, &Style {Bg: Blue, Fg: White, selected: true}) + } + Cursor() + if !t.selectionBefore { + Text(t.selection, &Style {Bg: Blue, Fg: White, selected: true}) + } + Text(t.afterCursor, nil) + Pop() } |
