-
Notifications
You must be signed in to change notification settings - Fork 1
/
editBox.go
185 lines (159 loc) · 5.32 KB
/
editBox.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package termboxUI
import (
"bytes"
"github.com/nsf/termbox-go"
)
//============================//
// Edit Box //
//----------------------------//
// An edit box or input box is a field that allows the user to input text.
// A custom type can be set to help indicate the nature of the text being input.
// For example, an input box could be for a first name or last name.
type EditBox struct {
Width int
Height int
Value []rune
Fg termbox.Attribute
Bg termbox.Attribute
CursorIndex int
CustomType uint16
}
// Creates a new instance of an edit box.
// When width is -1, the text box will be the width of the terminal window.
func CreateEditBox(width int, value string, customMessageCode uint16, fg, bg termbox.Attribute) *EditBox {
editBox := new(EditBox)
screenWidth, _ := termbox.Size()
editBox.Width = width
if editBox.Width == -1 {
editBox.Width = screenWidth
}
editBox.Fg = fg
editBox.Bg = bg
editBox.CustomType = customMessageCode
if len(value) > 0 {
editBox.Value = make([]rune, len(value))
copy(editBox.Value, []rune(value))
} else {
editBox.Value = make([]rune, 0)
}
editBox.CursorIndex = 0
return editBox
}
func (eb *EditBox) Draw(x, y int) {
// Convert the input editable string to an array of runes so characters can be added, modified or removed from the edit text.
if len(eb.Value) > eb.Width {
nchrs := make([]rune, eb.Width)
copy(nchrs, eb.Value[len(eb.Value)-eb.Width-1:])
eb.Value = nchrs
}
displayString := string(eb.Value)
textbox := CreateTextBox(eb.Width, 4, false, false, TextAlignmentDefault, TextAlignmentCenter, eb.Fg, eb.Bg)
textbox.AddText("/> " + displayString)
textbox.Draw(x, y)
x_coord := x + eb.CursorIndex + 3
termbox.SetCursor(x_coord, y+2)
return
}
// Handles a termbox key or character input
// 'Enter' or 'Return' will end the string editing and signal that editing is complete.
// 'Backspace' removes the character before the currently selected character.
// 'Delete' removes the currently selected character.
// Left and right arrow keys will move the cursor along the edit string.
// 'Tab' inserts four spaces to the run array.
// 'Space' inserts a single space.
// Otherwise the character input is added to the string.
func (eb *EditBox) HandleKey(key termbox.Key, ch rune, ev chan UIEvent) (eventConsumed bool) {
eventConsumed = true
switch key {
case termbox.KeyEnter:
// Send along the input
event := UIEvent{}
event.Type = UIResultString
event.CustomType = eb.CustomType
event.Data = bytes.NewBufferString(string(eb.Value))
ev <- event
//Clear the edit buffer
eb.Value = make([]rune, 0)
eb.CursorIndex = 0
case termbox.KeyBackspace2:
startLength := len(eb.Value)
eb.Value = removeCharacter(eb.Value, eb.CursorIndex-1)
if startLength > len(eb.Value) {
eb.CursorIndex = setCursor(eb.CursorIndex, eb.CursorIndex-1, len(eb.Value))
}
case termbox.KeyDelete:
eb.Value = removeCharacter(eb.Value, eb.CursorIndex)
case termbox.KeyArrowRight:
eb.CursorIndex = setCursor(eb.CursorIndex, eb.CursorIndex+1, len(eb.Value))
case termbox.KeyArrowLeft:
eb.CursorIndex = setCursor(eb.CursorIndex, eb.CursorIndex-1, len(eb.Value))
case termbox.KeyTab:
startLength := len(eb.Value)
eb.Value = insertCharacter(eb.Value, ' ', eb.CursorIndex)
eb.Value = insertCharacter(eb.Value, ' ', eb.CursorIndex)
eb.Value = insertCharacter(eb.Value, ' ', eb.CursorIndex)
eb.Value = insertCharacter(eb.Value, ' ', eb.CursorIndex)
if startLength < len(eb.Value) {
eb.CursorIndex = setCursor(eb.CursorIndex, eb.CursorIndex+4, len(eb.Value))
}
case termbox.KeySpace:
startLength := len(eb.Value)
eb.Value = insertCharacter(eb.Value, ' ', eb.CursorIndex)
if startLength < len(eb.Value) {
eb.CursorIndex = setCursor(eb.CursorIndex, eb.CursorIndex+1, len(eb.Value))
}
default:
if ch != 0 {
startLength := len(eb.Value)
eb.Value = insertCharacter(eb.Value, ch, eb.CursorIndex)
if startLength < len(eb.Value) {
eb.CursorIndex = setCursor(eb.CursorIndex, eb.CursorIndex+1, len(eb.Value))
}
} else {
eventConsumed = false
}
}
return
}
//============================//
// Utilities //
//----------------------------//
// Insert a character into the rune array at a specified index.
// If the index is greater than the length of the edit rune array, it will be appended to the end of the array.
func insertCharacter(dst []rune, ch rune, index int) []rune {
slice := make([]rune, len(dst)+1)
switch {
case index == len(dst):
//add the character to the end of the array
copy(slice, dst)
slice[len(dst)] = ch
return slice
case len(dst) > index:
// insert the character into the array
copy(slice, dst[:index])
slice[index] = ch
copy(slice[index+1:], dst[index:])
return slice
default:
return dst
}
}
// Remove the character from the rune array that is specifid by 'index.' if it is a valide character within the edit string.
func removeCharacter(dst []rune, index int) []rune {
if index >= 0 && index < len(dst) {
slice := make([]rune, len(dst)-1)
copy(slice, dst[:index])
copy(slice[index:], dst[index+1:])
return slice
}
return dst
}
// Determine the index of the active/highlighted character in the edit string.
func setCursor(from, to, inputBoxLength int) (newIndex int) {
newIndex = from
if from < to && from >= 0 ||
from > to && to >= 0 {
newIndex = to
}
return
}