summaryrefslogtreecommitdiff
path: root/tui/text_input.go
diff options
context:
space:
mode:
authorcitrons <citrons@mondecitronne.com>2025-05-31 17:16:38 -0500
committercitrons <citrons@mondecitronne.com>2025-05-31 17:16:57 -0500
commitb11c892158772f508e494b2726a5d4db1bb74d23 (patch)
tree16113353422520259ccc7937e9085a68ac6663a6 /tui/text_input.go
parent0a58e68ad438ff43fa5bbecdb8914aa00cab5099 (diff)
text input box
Diffstat (limited to 'tui/text_input.go')
-rw-r--r--tui/text_input.go140
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()
}