...
1 package cbind
2
3 import (
4 "errors"
5 "strings"
6
7 "github.com/gdamore/tcell/v3"
8 )
9
10
11 const (
12 LabelCtrl = "ctrl"
13 LabelAlt = "alt"
14 LabelMeta = "meta"
15 LabelShift = "shift"
16 )
17
18
19 var ErrInvalidKeyEvent = errors.New("invalid key event")
20
21
22
23
24 var UnifyEnterKeys = true
25
26 var fullKeyNames = map[string]string{
27 "backspace2": "Backspace",
28 "pgup": "PageUp",
29 "pgdn": "PageDown",
30 "esc": "Escape",
31 }
32
33
34 func Decode(s string) (mod tcell.ModMask, key tcell.Key, str string, err error) {
35 if len(s) == 0 {
36 return 0, 0, "", ErrInvalidKeyEvent
37 }
38
39
40 if s[len(s)-1:] == "+" {
41 key = tcell.KeyRune
42 str = "+"
43
44 if len(s) == 1 {
45 return mod, key, str, nil
46 } else if len(s) == 2 {
47 return 0, 0, "", ErrInvalidKeyEvent
48 } else {
49 s = s[:len(s)-2]
50 }
51 }
52
53 split := strings.Split(s, "+")
54 DECODEPIECE:
55 for _, piece := range split {
56
57 pieceLower := strings.ToLower(piece)
58 switch pieceLower {
59 case LabelCtrl:
60 mod |= tcell.ModCtrl
61 continue
62 case LabelAlt:
63 mod |= tcell.ModAlt
64 continue
65 case LabelMeta:
66 mod |= tcell.ModMeta
67 continue
68 case LabelShift:
69 mod |= tcell.ModShift
70 continue
71 }
72
73
74 for shortKey, fullKey := range fullKeyNames {
75 if pieceLower == strings.ToLower(fullKey) {
76 pieceLower = shortKey
77 break
78 }
79 }
80 switch pieceLower {
81 case "backspace":
82 key = tcell.KeyBackspace2
83 continue
84 case "space", "spacebar":
85 key = tcell.KeyRune
86 str = " "
87 continue
88 }
89 for k, keyName := range tcell.KeyNames {
90 if pieceLower == strings.ToLower(strings.ReplaceAll(keyName, "-", "+")) {
91 key = k
92 if key < 0x80 {
93 str = string(rune(k))
94 }
95 continue DECODEPIECE
96 }
97 }
98
99
100 if len(piece) > 1 {
101 return 0, 0, "", ErrInvalidKeyEvent
102 }
103
104 key = tcell.KeyRune
105 str = string(rune(piece[0]))
106 }
107
108
109 if mod&tcell.ModCtrl != 0 && key == tcell.KeyRune {
110 str = strings.ToLower(str)
111 }
112
113 return mod, key, str, nil
114 }
115
116
117 func Encode(mod tcell.ModMask, key tcell.Key, str string) (string, error) {
118 var b strings.Builder
119 var wrote bool
120
121 if mod&tcell.ModCtrl != 0 {
122 if key == tcell.KeyBackspace || key == tcell.KeyTab || key == tcell.KeyEnter {
123 mod ^= tcell.ModCtrl
124 } else {
125
126 if key >= tcell.KeyCtrlA && key <= tcell.KeyCtrlZ {
127 mod |= tcell.ModCtrl
128 str = string(rune('a' + (key - tcell.KeyCtrlA)))
129 key = tcell.KeyRune
130 }
131 }
132 }
133
134 if key != tcell.KeyRune {
135 if UnifyEnterKeys && key == tcell.KeyCtrlJ {
136 key = tcell.KeyEnter
137 } else if key < 0x80 {
138 str = string(rune(key))
139 }
140 }
141
142
143 if mod&tcell.ModCtrl != 0 {
144 b.WriteString(upperFirst(LabelCtrl))
145 wrote = true
146 }
147 if mod&tcell.ModAlt != 0 {
148 if wrote {
149 b.WriteRune('+')
150 }
151 b.WriteString(upperFirst(LabelAlt))
152 wrote = true
153 }
154 if mod&tcell.ModMeta != 0 {
155 if wrote {
156 b.WriteRune('+')
157 }
158 b.WriteString(upperFirst(LabelMeta))
159 wrote = true
160 }
161 if mod&tcell.ModShift != 0 {
162 if wrote {
163 b.WriteRune('+')
164 }
165 b.WriteString(upperFirst(LabelShift))
166 wrote = true
167 }
168
169 if key == tcell.KeyRune && str == " " {
170 if wrote {
171 b.WriteRune('+')
172 }
173 b.WriteString("Space")
174 } else if key != tcell.KeyRune {
175
176 keyName := tcell.KeyNames[key]
177 if keyName == "" {
178 return "", ErrInvalidKeyEvent
179 }
180 keyName = strings.ReplaceAll(keyName, "-", "+")
181 fullKeyName := fullKeyNames[strings.ToLower(keyName)]
182 if fullKeyName != "" {
183 keyName = fullKeyName
184 }
185
186 if wrote {
187 b.WriteRune('+')
188 }
189 b.WriteString(keyName)
190 } else {
191
192 if wrote {
193 b.WriteRune('+')
194 }
195 b.WriteString(str)
196 }
197
198 return b.String(), nil
199 }
200
201 func upperFirst(s string) string {
202 if len(s) <= 1 {
203 return strings.ToUpper(s)
204 }
205 return strings.ToUpper(s[:1]) + s[1:]
206 }
207
View as plain text