Initial commit, lots of cleanup and stuff to do, it may not work.

This commit is contained in:
Storm Dragon
2025-01-15 23:43:44 -05:00
commit 3f0246a4f8
80 changed files with 11306 additions and 0 deletions

25
uiterm/attributes.go Normal file
View File

@ -0,0 +1,25 @@
package uiterm
/*
* Source: https://godoc.org/github.com/nsf/termbox-go
*/
type Attribute int
const (
ColorDefault Attribute = iota
ColorBlack
ColorRed
ColorGreen
ColorYellow
ColorBlue
ColorMagenta
ColorCyan
ColorWhite
)
const (
AttrBold Attribute = 1 << (iota + 4)
AttrUnderline
AttrReverse
)

1
uiterm/doc.go Normal file
View File

@ -0,0 +1 @@
package uiterm // import "git.2mb.codes/~cmb/barnard/uiterm"

253
uiterm/key_enumer.go Normal file
View File

@ -0,0 +1,253 @@
// Code generated by "enumer -type=Key -trimprefix=Key -yaml -json -transform=snake"; DO NOT EDIT.
//
package uiterm
import (
"encoding/json"
"fmt"
)
const (
_KeyName_0 = "ctrl_tildectrl_actrl_bctrl_cctrl_dctrl_ectrl_fctrl_gbackspacetabctrl_jctrl_kctrl_lenterctrl_nctrl_octrl_pctrl_qctrl_rctrl_sctrl_tctrl_uctrl_vctrl_wctrl_xctrl_yctrl_zescctrl4ctrl5ctrl6ctrl7space"
_KeyName_1 = "backspace2"
_KeyName_2 = "mouse_rightmouse_middlemouse_leftarrow_rightarrow_leftarrow_downarrow_uppgdnpgupendhomedeleteinsertf12f11f10f9f8f7f6f5f4f3f2f1alt_ctrl_tildealt_ctrl_aalt_ctrl_balt_ctrl_calt_ctrl_dalt_ctrl_ealt_ctrl_falt_ctrl_galt_backspacealt_tabalt_ctrl_jalt_ctrl_kalt_ctrl_lalt_enteralt_ctrl_nalt_ctrl_oalt_ctrl_palt_ctrl_qalt_ctrl_ralt_ctrl_salt_ctrl_talt_ctrl_ualt_ctrl_valt_ctrl_walt_ctrl_xalt_ctrl_yalt_ctrl_zalt_escalt_ctrl4alt_ctrl5alt_ctrl6alt_ctrl7alt_space"
_KeyName_3 = "alt_aalt_balt_calt_dalt_ealt_falt_galt_halt_ialt_jalt_kalt_lalt_malt_nalt_oalt_palt_qalt_ralt_salt_talt_ualt_valt_walt_xalt_yalt_z"
_KeyName_4 = "alt_backspace2"
_KeyName_5 = "alt_arrow_rightalt_arrow_leftalt_arrow_downalt_arrow_upalt_pgdnalt_pgupalt_endalt_homealt_deletealt_insertalt_f12alt_f11alt_f10alt_f9alt_f8alt_f7alt_f6alt_f5alt_f4alt_f3alt_f2alt_f1"
)
var (
_KeyIndex_0 = [...]uint8{0, 10, 16, 22, 28, 34, 40, 46, 52, 61, 64, 70, 76, 82, 87, 93, 99, 105, 111, 117, 123, 129, 135, 141, 147, 153, 159, 165, 168, 173, 178, 183, 188, 193}
_KeyIndex_1 = [...]uint8{0, 10}
_KeyIndex_2 = [...]uint16{0, 11, 23, 33, 44, 54, 64, 72, 76, 80, 83, 87, 93, 99, 102, 105, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 140, 150, 160, 170, 180, 190, 200, 210, 223, 230, 240, 250, 260, 269, 279, 289, 299, 309, 319, 329, 339, 349, 359, 369, 379, 389, 399, 406, 415, 424, 433, 442, 451}
_KeyIndex_3 = [...]uint8{0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130}
_KeyIndex_4 = [...]uint8{0, 14}
_KeyIndex_5 = [...]uint8{0, 15, 29, 43, 55, 63, 71, 78, 86, 96, 106, 113, 120, 127, 133, 139, 145, 151, 157, 163, 169, 175, 181}
)
func (i Key) String() string {
switch {
case 0 <= i && i <= 32:
return _KeyName_0[_KeyIndex_0[i]:_KeyIndex_0[i+1]]
case i == 127:
return _KeyName_1
case 65511 <= i && i <= 65568:
i -= 65511
return _KeyName_2[_KeyIndex_2[i]:_KeyIndex_2[i+1]]
case 65633 <= i && i <= 65658:
i -= 65633
return _KeyName_3[_KeyIndex_3[i]:_KeyIndex_3[i+1]]
case i == 65663:
return _KeyName_4
case 131050 <= i && i <= 131071:
i -= 131050
return _KeyName_5[_KeyIndex_5[i]:_KeyIndex_5[i+1]]
default:
return fmt.Sprintf("Key(%d)", i)
}
}
var _KeyValues = []Key{0, 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, 127, 65511, 65512, 65513, 65514, 65515, 65516, 65517, 65518, 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, 65531, 65532, 65533, 65534, 65535, 65536, 65537, 65538, 65539, 65540, 65541, 65542, 65543, 65544, 65545, 65546, 65547, 65548, 65549, 65550, 65551, 65552, 65553, 65554, 65555, 65556, 65557, 65558, 65559, 65560, 65561, 65562, 65563, 65564, 65565, 65566, 65567, 65568, 65633, 65634, 65635, 65636, 65637, 65638, 65639, 65640, 65641, 65642, 65643, 65644, 65645, 65646, 65647, 65648, 65649, 65650, 65651, 65652, 65653, 65654, 65655, 65656, 65657, 65658, 65663, 131050, 131051, 131052, 131053, 131054, 131055, 131056, 131057, 131058, 131059, 131060, 131061, 131062, 131063, 131064, 131065, 131066, 131067, 131068, 131069, 131070, 131071}
var _KeyNameToValueMap = map[string]Key{
_KeyName_0[0:10]: 0,
_KeyName_0[10:16]: 1,
_KeyName_0[16:22]: 2,
_KeyName_0[22:28]: 3,
_KeyName_0[28:34]: 4,
_KeyName_0[34:40]: 5,
_KeyName_0[40:46]: 6,
_KeyName_0[46:52]: 7,
_KeyName_0[52:61]: 8,
_KeyName_0[61:64]: 9,
_KeyName_0[64:70]: 10,
_KeyName_0[70:76]: 11,
_KeyName_0[76:82]: 12,
_KeyName_0[82:87]: 13,
_KeyName_0[87:93]: 14,
_KeyName_0[93:99]: 15,
_KeyName_0[99:105]: 16,
_KeyName_0[105:111]: 17,
_KeyName_0[111:117]: 18,
_KeyName_0[117:123]: 19,
_KeyName_0[123:129]: 20,
_KeyName_0[129:135]: 21,
_KeyName_0[135:141]: 22,
_KeyName_0[141:147]: 23,
_KeyName_0[147:153]: 24,
_KeyName_0[153:159]: 25,
_KeyName_0[159:165]: 26,
_KeyName_0[165:168]: 27,
_KeyName_0[168:173]: 28,
_KeyName_0[173:178]: 29,
_KeyName_0[178:183]: 30,
_KeyName_0[183:188]: 31,
_KeyName_0[188:193]: 32,
_KeyName_1[0:10]: 127,
_KeyName_2[0:11]: 65511,
_KeyName_2[11:23]: 65512,
_KeyName_2[23:33]: 65513,
_KeyName_2[33:44]: 65514,
_KeyName_2[44:54]: 65515,
_KeyName_2[54:64]: 65516,
_KeyName_2[64:72]: 65517,
_KeyName_2[72:76]: 65518,
_KeyName_2[76:80]: 65519,
_KeyName_2[80:83]: 65520,
_KeyName_2[83:87]: 65521,
_KeyName_2[87:93]: 65522,
_KeyName_2[93:99]: 65523,
_KeyName_2[99:102]: 65524,
_KeyName_2[102:105]: 65525,
_KeyName_2[105:108]: 65526,
_KeyName_2[108:110]: 65527,
_KeyName_2[110:112]: 65528,
_KeyName_2[112:114]: 65529,
_KeyName_2[114:116]: 65530,
_KeyName_2[116:118]: 65531,
_KeyName_2[118:120]: 65532,
_KeyName_2[120:122]: 65533,
_KeyName_2[122:124]: 65534,
_KeyName_2[124:126]: 65535,
_KeyName_2[126:140]: 65536,
_KeyName_2[140:150]: 65537,
_KeyName_2[150:160]: 65538,
_KeyName_2[160:170]: 65539,
_KeyName_2[170:180]: 65540,
_KeyName_2[180:190]: 65541,
_KeyName_2[190:200]: 65542,
_KeyName_2[200:210]: 65543,
_KeyName_2[210:223]: 65544,
_KeyName_2[223:230]: 65545,
_KeyName_2[230:240]: 65546,
_KeyName_2[240:250]: 65547,
_KeyName_2[250:260]: 65548,
_KeyName_2[260:269]: 65549,
_KeyName_2[269:279]: 65550,
_KeyName_2[279:289]: 65551,
_KeyName_2[289:299]: 65552,
_KeyName_2[299:309]: 65553,
_KeyName_2[309:319]: 65554,
_KeyName_2[319:329]: 65555,
_KeyName_2[329:339]: 65556,
_KeyName_2[339:349]: 65557,
_KeyName_2[349:359]: 65558,
_KeyName_2[359:369]: 65559,
_KeyName_2[369:379]: 65560,
_KeyName_2[379:389]: 65561,
_KeyName_2[389:399]: 65562,
_KeyName_2[399:406]: 65563,
_KeyName_2[406:415]: 65564,
_KeyName_2[415:424]: 65565,
_KeyName_2[424:433]: 65566,
_KeyName_2[433:442]: 65567,
_KeyName_2[442:451]: 65568,
_KeyName_3[0:5]: 65633,
_KeyName_3[5:10]: 65634,
_KeyName_3[10:15]: 65635,
_KeyName_3[15:20]: 65636,
_KeyName_3[20:25]: 65637,
_KeyName_3[25:30]: 65638,
_KeyName_3[30:35]: 65639,
_KeyName_3[35:40]: 65640,
_KeyName_3[40:45]: 65641,
_KeyName_3[45:50]: 65642,
_KeyName_3[50:55]: 65643,
_KeyName_3[55:60]: 65644,
_KeyName_3[60:65]: 65645,
_KeyName_3[65:70]: 65646,
_KeyName_3[70:75]: 65647,
_KeyName_3[75:80]: 65648,
_KeyName_3[80:85]: 65649,
_KeyName_3[85:90]: 65650,
_KeyName_3[90:95]: 65651,
_KeyName_3[95:100]: 65652,
_KeyName_3[100:105]: 65653,
_KeyName_3[105:110]: 65654,
_KeyName_3[110:115]: 65655,
_KeyName_3[115:120]: 65656,
_KeyName_3[120:125]: 65657,
_KeyName_3[125:130]: 65658,
_KeyName_4[0:14]: 65663,
_KeyName_5[0:15]: 131050,
_KeyName_5[15:29]: 131051,
_KeyName_5[29:43]: 131052,
_KeyName_5[43:55]: 131053,
_KeyName_5[55:63]: 131054,
_KeyName_5[63:71]: 131055,
_KeyName_5[71:78]: 131056,
_KeyName_5[78:86]: 131057,
_KeyName_5[86:96]: 131058,
_KeyName_5[96:106]: 131059,
_KeyName_5[106:113]: 131060,
_KeyName_5[113:120]: 131061,
_KeyName_5[120:127]: 131062,
_KeyName_5[127:133]: 131063,
_KeyName_5[133:139]: 131064,
_KeyName_5[139:145]: 131065,
_KeyName_5[145:151]: 131066,
_KeyName_5[151:157]: 131067,
_KeyName_5[157:163]: 131068,
_KeyName_5[163:169]: 131069,
_KeyName_5[169:175]: 131070,
_KeyName_5[175:181]: 131071,
}
// KeyString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func KeyString(s string) (Key, error) {
if val, ok := _KeyNameToValueMap[s]; ok {
return val, nil
}
return 0, fmt.Errorf("%s does not belong to Key values", s)
}
// KeyValues returns all values of the enum
func KeyValues() []Key {
return _KeyValues
}
// IsAKey returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Key) IsAKey() bool {
for _, v := range _KeyValues {
if i == v {
return true
}
}
return false
}
// MarshalJSON implements the json.Marshaler interface for Key
func (i Key) MarshalJSON() ([]byte, error) {
return json.Marshal(i.String())
}
// UnmarshalJSON implements the json.Unmarshaler interface for Key
func (i *Key) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("Key should be a string, got %s", data)
}
var err error
*i, err = KeyString(s)
return err
}
// MarshalYAML implements a YAML Marshaler for Key
func (i Key) MarshalYAML() (interface{}, error) {
return i.String(), nil
}
// UnmarshalYAML implements a YAML Unmarshaler for Key
func (i *Key) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
}
var err error
*i, err = KeyString(s)
return err
}

186
uiterm/keys.go Normal file
View File

@ -0,0 +1,186 @@
package uiterm
//go:generate enumer -type=Key -trimprefix=Key -yaml -json -transform=snake
/*
* Source: https://godoc.org/github.com/nsf/termbox-go
*/
type Key uint32
const (
KeyF1 Key = 0xFFFF - iota
KeyF2
KeyF3
KeyF4
KeyF5
KeyF6
KeyF7
KeyF8
KeyF9
KeyF10
KeyF11
KeyF12
KeyInsert
KeyDelete
KeyHome
KeyEnd
KeyPgup
KeyPgdn
KeyArrowUp
KeyArrowDown
KeyArrowLeft
KeyArrowRight
MouseLeft
MouseMiddle
MouseRight
)
const (
KeyCtrlTilde Key = 0x00
KeyCtrl2 Key = 0x00
KeyCtrlSpace Key = 0x00
KeyCtrlA Key = 0x01
KeyCtrlB Key = 0x02
KeyCtrlC Key = 0x03
KeyCtrlD Key = 0x04
KeyCtrlE Key = 0x05
KeyCtrlF Key = 0x06
KeyCtrlG Key = 0x07
KeyBackspace Key = 0x08
KeyCtrlH Key = 0x08
KeyTab Key = 0x09
KeyCtrlI Key = 0x09
KeyCtrlJ Key = 0x0A
KeyCtrlK Key = 0x0B
KeyCtrlL Key = 0x0C
KeyEnter Key = 0x0D
KeyCtrlM Key = 0x0D
KeyCtrlN Key = 0x0E
KeyCtrlO Key = 0x0F
KeyCtrlP Key = 0x10
KeyCtrlQ Key = 0x11
KeyCtrlR Key = 0x12
KeyCtrlS Key = 0x13
KeyCtrlT Key = 0x14
KeyCtrlU Key = 0x15
KeyCtrlV Key = 0x16
KeyCtrlW Key = 0x17
KeyCtrlX Key = 0x18
KeyCtrlY Key = 0x19
KeyCtrlZ Key = 0x1A
KeyEsc Key = 0x1B
KeyCtrlLsqBracket Key = 0x1B
KeyCtrl3 Key = 0x1B
KeyCtrl4 Key = 0x1C
KeyCtrlBackslash Key = 0x1C
KeyCtrl5 Key = 0x1D
KeyCtrlRsqBracket Key = 0x1D
KeyCtrl6 Key = 0x1E
KeyCtrl7 Key = 0x1F
KeyCtrlSlash Key = 0x1F
KeyCtrlUnderscore Key = 0x1F
KeySpace Key = 0x20
KeyBackspace2 Key = 0x7F
KeyCtrl8 Key = 0x7F
)
//##altkeys##
const(
KeyAltF1 Key = KeyF1 + (1<<16)
KeyAltF2 Key = KeyF2 + (1<<16)
KeyAltF3 Key = KeyF3 + (1<<16)
KeyAltF4 Key = KeyF4 + (1<<16)
KeyAltF5 Key = KeyF5 + (1<<16)
KeyAltF6 Key = KeyF6 + (1<<16)
KeyAltF7 Key = KeyF7 + (1<<16)
KeyAltF8 Key = KeyF8 + (1<<16)
KeyAltF9 Key = KeyF9 + (1<<16)
KeyAltF10 Key = KeyF10 + (1<<16)
KeyAltF11 Key = KeyF11 + (1<<16)
KeyAltF12 Key = KeyF12 + (1<<16)
KeyAltInsert Key = KeyInsert + (1<<16)
KeyAltDelete Key = KeyDelete + (1<<16)
KeyAltHome Key = KeyHome + (1<<16)
KeyAltEnd Key = KeyEnd + (1<<16)
KeyAltPgup Key = KeyPgup + (1<<16)
KeyAltPgdn Key = KeyPgdn + (1<<16)
KeyAltArrowUp Key = KeyArrowUp + (1<<16)
KeyAltArrowDown Key = KeyArrowDown + (1<<16)
KeyAltArrowLeft Key = KeyArrowLeft + (1<<16)
KeyAltArrowRight Key = KeyArrowRight + (1<<16)
KeyAltCtrlTilde Key = KeyCtrlTilde + (1<<16)
KeyAltCtrl2 Key = KeyCtrl2 + (1<<16)
KeyAltCtrlSpace Key = KeyCtrlSpace + (1<<16)
KeyAltCtrlA Key = KeyCtrlA + (1<<16)
KeyAltCtrlB Key = KeyCtrlB + (1<<16)
KeyAltCtrlC Key = KeyCtrlC + (1<<16)
KeyAltCtrlD Key = KeyCtrlD + (1<<16)
KeyAltCtrlE Key = KeyCtrlE + (1<<16)
KeyAltCtrlF Key = KeyCtrlF + (1<<16)
KeyAltCtrlG Key = KeyCtrlG + (1<<16)
KeyAltBackspace Key = KeyBackspace + (1<<16)
KeyAltCtrlH Key = KeyCtrlH + (1<<16)
KeyAltTab Key = KeyTab + (1<<16)
KeyAltCtrlI Key = KeyCtrlI + (1<<16)
KeyAltCtrlJ Key = KeyCtrlJ + (1<<16)
KeyAltCtrlK Key = KeyCtrlK + (1<<16)
KeyAltCtrlL Key = KeyCtrlL + (1<<16)
KeyAltEnter Key = KeyEnter + (1<<16)
KeyAltCtrlM Key = KeyCtrlM + (1<<16)
KeyAltCtrlN Key = KeyCtrlN + (1<<16)
KeyAltCtrlO Key = KeyCtrlO + (1<<16)
KeyAltCtrlP Key = KeyCtrlP + (1<<16)
KeyAltCtrlQ Key = KeyCtrlQ + (1<<16)
KeyAltCtrlR Key = KeyCtrlR + (1<<16)
KeyAltCtrlS Key = KeyCtrlS + (1<<16)
KeyAltCtrlT Key = KeyCtrlT + (1<<16)
KeyAltCtrlU Key = KeyCtrlU + (1<<16)
KeyAltCtrlV Key = KeyCtrlV + (1<<16)
KeyAltCtrlW Key = KeyCtrlW + (1<<16)
KeyAltCtrlX Key = KeyCtrlX + (1<<16)
KeyAltCtrlY Key = KeyCtrlY + (1<<16)
KeyAltCtrlZ Key = KeyCtrlZ + (1<<16)
KeyAltEsc Key = KeyEsc + (1<<16)
KeyAltCtrlLsqBracket Key = KeyCtrlLsqBracket + (1<<16)
KeyAltCtrl3 Key = KeyCtrl3 + (1<<16)
KeyAltCtrl4 Key = KeyCtrl4 + (1<<16)
KeyAltCtrlBackslash Key = KeyCtrlBackslash + (1<<16)
KeyAltCtrl5 Key = KeyCtrl5 + (1<<16)
KeyAltCtrlRsqBracket Key = KeyCtrlRsqBracket + (1<<16)
KeyAltCtrl6 Key = KeyCtrl6 + (1<<16)
KeyAltCtrl7 Key = KeyCtrl7 + (1<<16)
KeyAltCtrlSlash Key = KeyCtrlSlash + (1<<16)
KeyAltCtrlUnderscore Key = KeyCtrlUnderscore + (1<<16)
KeyAltSpace Key = KeySpace + (1<<16)
KeyAltBackspace2 Key = KeyBackspace2 + (1<<16)
KeyAltCtrl8 Key = KeyCtrl8 + (1<<16)
KeyAltA Key = 0x61 + (1<<16)
KeyAltB Key = 0x62 + (1<<16)
KeyAltC Key = 0x63 + (1<<16)
KeyAltD Key = 0x64 + (1<<16)
KeyAltE Key = 0x65 + (1<<16)
KeyAltF Key = 0x66 + (1<<16)
KeyAltG Key = 0x67 + (1<<16)
KeyAltH Key = 0x68 + (1<<16)
KeyAltI Key = 0x69 + (1<<16)
KeyAltJ Key = 0x6a + (1<<16)
KeyAltK Key = 0x6b + (1<<16)
KeyAltL Key = 0x6c + (1<<16)
KeyAltM Key = 0x6d + (1<<16)
KeyAltN Key = 0x6e + (1<<16)
KeyAltO Key = 0x6f + (1<<16)
KeyAltP Key = 0x70 + (1<<16)
KeyAltQ Key = 0x71 + (1<<16)
KeyAltR Key = 0x72 + (1<<16)
KeyAltS Key = 0x73 + (1<<16)
KeyAltT Key = 0x74 + (1<<16)
KeyAltU Key = 0x75 + (1<<16)
KeyAltV Key = 0x76 + (1<<16)
KeyAltW Key = 0x77 + (1<<16)
KeyAltX Key = 0x78 + (1<<16)
KeyAltY Key = 0x79 + (1<<16)
KeyAltZ Key = 0x7a + (1<<16)
)

54
uiterm/label.go Normal file
View File

@ -0,0 +1,54 @@
package uiterm
import (
"strings"
"github.com/nsf/termbox-go"
)
type Label struct {
Text string
Fg, Bg Attribute
ui *Ui
x0, y0, x1, y1 int
}
func (l *Label) uiInitialize(ui *Ui) {
l.ui = ui
}
func (l *Label) uiSetActive(active bool) {
}
func (l *Label) uiSetBounds(x0, y0, x1, y1 int) {
l.x0 = x0
l.y0 = y0
l.x1 = x1
l.y1 = y1
l.uiDraw()
}
func (l *Label) uiDraw() {
l.ui.beginDraw()
defer l.ui.endDraw()
reader := strings.NewReader(l.Text)
for y := l.y0; y < l.y1; y++ {
for x := l.x0; x < l.x1; x++ {
var chr rune
if ch, _, err := reader.ReadRune(); err != nil {
chr = ' '
} else {
chr = ch
}
termbox.SetCell(x, y, chr, termbox.Attribute(l.Fg), termbox.Attribute(l.Bg))
}
}
}
func (l *Label) uiKeyEvent(key Key) {
}
func (l *Label) uiCharacterEvent(chr rune) {
}

138
uiterm/textbox.go Normal file
View File

@ -0,0 +1,138 @@
package uiterm
import (
"strings"
// "unicode/utf8"
"github.com/nsf/termbox-go"
)
type Textbox struct {
Text string
Fg, Bg Attribute
Input func(ui *Ui, textbox *Textbox, text string)
ui *Ui
active bool
x0, y0, x1, y1 int
pos int
}
func (t *Textbox) uiInitialize(ui *Ui) {
t.ui = ui
t.pos = 0
}
func (t *Textbox) uiSetActive(active bool) {
t.active = active
t.uiDraw()
}
func (t *Textbox) uiSetBounds(x0, y0, x1, y1 int) {
t.x0 = x0
t.y0 = y0
t.x1 = x1
t.y1 = y1
t.uiDraw()
}
func (t *Textbox) uiDraw() {
t.ui.beginDraw()
defer t.ui.endDraw()
reader := strings.NewReader(t.Text)
if t.pos < 0 {
t.pos = 0
}
if t.pos > len(t.Text) {
t.pos = len(t.Text)
}
for y := t.y0; y < t.y1; y++ {
for x := t.x0; x < t.x1; x++ {
var chr rune
if ch, _, err := reader.ReadRune(); err != nil {
chr = ' '
} else {
chr = ch
}
termbox.SetCell(x, y, chr, termbox.Attribute(t.Fg), termbox.Attribute(t.Bg))
}
}
if t.active {
var x = 0
var y = 0
var idx = -1
var flag = false
for y = t.y0; y < t.y1; y++ {
for x = t.x0; x < t.x1; x++ {
idx += 1
if idx == t.pos {
flag = true
}
if flag == true {
break
}
}
if flag == true {
break
}
}
termbox.SetCursor(x, y)
}
}
func (t *Textbox) uiKeyEvent(key Key) {
redraw := false
switch key {
case KeyHome:
t.pos = 0
redraw = true
case KeyEnd:
t.pos = len(t.Text)
redraw = true
case KeyArrowLeft:
t.pos -= 1
redraw = true
case KeyArrowRight:
t.pos += 1
redraw = true
case KeyCtrlC:
t.Text = ""
t.pos = 0
redraw = true
case KeyEnter:
if t.Input != nil {
t.Input(t.ui, t, t.Text)
}
t.Text = ""
t.pos = 0
redraw = true
case KeySpace:
t.uiCharacterEvent(' ')
case KeyBackspace:
case KeyBackspace2:
if len(t.Text) > 0 {
if t.pos > 0 {
t.Text = t.Text[:t.pos-1] + t.Text[t.pos:]
t.pos -= 1
}
}
// if r, size := utf8.DecodeLastRuneInString(t.Text); r != utf8.RuneError {
// t.Text = t.Text[:len(t.Text)-size]
//t.pos-=size
redraw = true
// }
// }
}
if redraw {
t.uiDraw()
}
}
func (t *Textbox) uiCharacterEvent(chr rune) {
var s = string(chr)
t.Text = t.Text[:t.pos] + s + t.Text[t.pos:]
t.pos += len(s)
t.uiDraw()
}

160
uiterm/textview.go Normal file
View File

@ -0,0 +1,160 @@
package uiterm
import (
"strings"
"github.com/nsf/termbox-go"
)
type Textview struct {
Lines []string
CurrentLine int
Fg, Bg Attribute
showTimestamps bool
parsedLines []string
ui *Ui
x0, y0, x1, y1 int
}
func (t *Textview) uiInitialize(ui *Ui) {
t.ui = ui
t.showTimestamps = true
}
func (t *Textview) ToggleTimestamps() {
if t.showTimestamps == true {
t.showTimestamps = false
} else {
t.showTimestamps = true
}
t.updateParsedLines()
t.uiDraw()
}
func (t *Textview) uiSetActive(active bool) {
}
func (t *Textview) uiSetBounds(x0, y0, x1, y1 int) {
t.x0 = x0
t.y0 = y0
t.x1 = x1
t.y1 = y1
t.updateParsedLines()
t.uiDraw()
}
func (t *Textview) ScrollUp() {
if newLine := t.CurrentLine + 1; newLine < len(t.parsedLines) {
t.CurrentLine = newLine
}
t.uiDraw()
}
func (t *Textview) ScrollDown() {
if newLine := t.CurrentLine - 1; newLine >= 0 {
t.CurrentLine = newLine
}
t.uiDraw()
}
func (t *Textview) ScrollTop() {
if newLine := len(t.parsedLines) - 1; newLine > 0 {
t.CurrentLine = newLine
} else {
t.CurrentLine = 0
}
t.uiDraw()
}
func (t *Textview) ScrollBottom() {
t.CurrentLine = 0
t.uiDraw()
}
func (t *Textview) updateParsedLines() {
width := t.x1 - t.x0
if t.Lines == nil || width <= 0 {
t.parsedLines = nil
return
}
parsed := make([]string, 0, len(t.Lines))
for _, line := range t.Lines {
var l = line
if t.showTimestamps == false {
l = strings.TrimSpace(strings.Split(line, "]")[1])
}
current := ""
chars := 0
reader := strings.NewReader(l)
for {
if chars >= width {
parsed = append(parsed, current)
chars = 0
current = ""
}
if reader.Len() <= 0 {
if chars > 0 {
parsed = append(parsed, current)
}
break
}
if ch, _, err := reader.ReadRune(); err == nil {
current = current + string(ch)
chars++
}
}
}
t.parsedLines = parsed
}
func (t *Textview) AddLine(line string) {
t.Lines = append(t.Lines, line)
t.updateParsedLines()
t.uiDraw()
}
func (t *Textview) Clear() {
t.Lines = nil
t.CurrentLine = 0
t.parsedLines = nil
t.uiDraw()
}
func (t *Textview) uiDraw() {
t.ui.beginDraw()
defer t.ui.endDraw()
var reader *strings.Reader
writeableLines := t.y1 - t.y0
lineNum := 0
if writeableLines < len(t.parsedLines) {
lineNum = len(t.parsedLines) - writeableLines
}
//Beep()
for y := t.y0; y < writeableLines; y++ {
if lineNum < len(t.parsedLines) {
reader = strings.NewReader(t.parsedLines[lineNum])
} else {
reader = nil
}
for x := t.x0; x < t.x1; x++ {
var chr rune = ' '
if reader != nil {
if ch, _, err := reader.ReadRune(); err == nil {
chr = ch
} //no err
} //reader != nil
termbox.SetCell(x, y, chr, termbox.Attribute(t.Fg), termbox.Attribute(t.Bg))
} //each x
lineNum++
} //each y
} //func
func (t *Textview) uiKeyEvent(key Key) {
}
func (t *Textview) uiCharacterEvent(chr rune) {
}

185
uiterm/tree.go Normal file
View File

@ -0,0 +1,185 @@
package uiterm
import (
"strings"
"github.com/nsf/termbox-go"
)
type TreeItem interface {
TreeItemStyle(fg, bg Attribute, active bool) (Attribute, Attribute)
String() string
}
type renderedTreeItem struct {
//String string
Level int
Item TreeItem
}
type Tree struct {
Fg, Bg Attribute
Generator func(item TreeItem) []TreeItem
KeyListener func(ui *Ui, tree *Tree, item TreeItem, key Key)
CharacterListener func(ui *Ui, tree *Tree, item TreeItem, chr rune)
lines []renderedTreeItem
activeLine int
ui *Ui
active bool
x0, y0, x1, y1 int
}
func bounded(i, lower, upper int) int {
if i < lower {
return lower
}
if i > upper {
return upper
}
return i
}
func (t *Tree) uiInitialize(ui *Ui) {
t.ui = ui
}
func (t *Tree) uiSetActive(active bool) {
t.active = active
t.uiDraw()
}
func (t *Tree) uiSetBounds(x0, y0, x1, y1 int) {
t.x0 = x0
t.y0 = y0
t.x1 = x1
t.y1 = y1
t.uiDraw()
}
func (t *Tree) Rebuild() {
if t.Generator == nil {
t.lines = []renderedTreeItem{}
return
}
lines := []renderedTreeItem{}
for _, item := range t.Generator(nil) {
children := t.rebuild_rec(item, 0)
if children != nil {
lines = append(lines, children...)
}
}
t.lines = lines
t.SetActiveLine(0, true)
t.uiDraw()
}
func (t *Tree) rebuild_rec(parent TreeItem, level int) []renderedTreeItem {
if parent == nil {
return nil
}
lines := []renderedTreeItem{
renderedTreeItem{
Level: level,
Item: parent,
},
}
for _, item := range t.Generator(parent) {
children := t.rebuild_rec(item, level+1)
if children != nil {
lines = append(lines, children...)
}
}
return lines
}
func (t *Tree) uiDraw() {
t.ui.beginDraw()
defer t.ui.endDraw()
if t.lines == nil {
t.Rebuild()
}
if t.y1-t.y0 <= 0 {
return
}
var line = t.activeLine
var height = t.y1 - t.y0
var startline = 0
//var total = len(t.lines)
//I'd welcome a better algorithm for this; for that matter, I'd love a book or reference for all sorts of GUI algorithms.
//if (startline+height) < line {
for startline = 0; (startline + height) <= line; startline += height {
}
//}
//if startline+height >= total {
//var rem=(startline+height)-total
//startline-=rem
//}
if startline < 0 {
startline = 0
}
line = startline
for y := t.y0; y < t.y1; y++ {
var reader *strings.Reader
var item TreeItem
level := 0
if line < len(t.lines) {
item = t.lines[line].Item
level = t.lines[line].Level
reader = strings.NewReader(item.String())
}
for x := t.x0; x < t.x1; x++ {
var chr rune = ' '
fg := t.Fg
bg := t.Bg
dx := x - t.x0
dy := y - t.y0
if reader != nil && level*2 <= dx {
if ch, _, err := reader.ReadRune(); err == nil {
chr = ch
fg, bg = item.TreeItemStyle(fg, bg, t.active && t.activeLine == dy)
}
}
termbox.SetCell(x, y, chr, termbox.Attribute(fg), termbox.Attribute(bg))
}
if t.activeLine == (line) {
termbox.SetCursor(t.x0, y)
}
line++
}
}
func (t *Tree) SetActiveLine(num int, relative bool) {
if relative {
t.activeLine = bounded(t.activeLine+num, 0, len(t.lines)-1)
} else {
t.activeLine = bounded(num, 0, len(t.lines)-1)
}
}
func (t *Tree) uiKeyEvent(key Key) {
var runHandler = true
switch key {
case KeyArrowUp:
t.SetActiveLine(-1, true)
runHandler = false
case KeyArrowDown:
t.SetActiveLine(1, true)
runHandler = false
}
if runHandler == true && t.KeyListener != nil {
t.KeyListener(t.ui, t, t.lines[t.activeLine].Item, key)
}
t.uiDraw()
}
func (t *Tree) uiCharacterEvent(ch rune) {
if t.CharacterListener != nil {
t.CharacterListener(t.ui, t, t.lines[t.activeLine].Item, ch)
}
}

218
uiterm/ui.go Normal file
View File

@ -0,0 +1,218 @@
package uiterm
import (
"errors"
"strings"
"sync/atomic"
"github.com/nsf/termbox-go"
)
type KeyListener func(ui *Ui, key Key)
type CommandListener func(ui *Ui, cmd string)
type UiManager interface {
OnUiInitialize(ui *Ui)
OnUiDoneInitialize(ui *Ui)
OnUiResize(ui *Ui, width, height int)
}
type Ui struct {
Fg, Bg Attribute
close chan bool
manager UiManager
drawCount int32
elements map[string]*uiElement
activeElement *uiElement
keyListeners map[Key][]KeyListener
commandListeners map[string][]CommandListener
}
type uiElement struct {
Name string
X0, Y0, X1, Y1 int
View View
}
func New(manager UiManager) *Ui {
ui := &Ui{
close: make(chan bool, 10),
elements: make(map[string]*uiElement),
manager: manager,
keyListeners: make(map[Key][]KeyListener),
commandListeners: make(map[string][]CommandListener),
}
return ui
}
func (ui *Ui) Close() {
if termbox.IsInit {
ui.close <- true
}
}
func (ui *Ui) Refresh() {
if termbox.IsInit {
ui.beginDraw()
defer ui.endDraw()
termbox.Clear(termbox.Attribute(ui.Fg), termbox.Attribute(ui.Bg))
termbox.HideCursor()
for _, element := range ui.elements {
element.View.uiDraw()
}
}
}
func (ui *Ui) beginDraw() {
atomic.AddInt32(&ui.drawCount, 1)
}
func (ui *Ui) endDraw() {
if count := atomic.AddInt32(&ui.drawCount, -1); count == 0 {
termbox.Flush()
}
}
func (ui *Ui) Active() string {
return ui.activeElement.Name
}
func (ui *Ui) SetActive(name string) {
element, _ := ui.elements[name]
if ui.activeElement != nil {
ui.activeElement.View.uiSetActive(false)
}
ui.activeElement = element
if element != nil {
element.View.uiSetActive(true)
}
}
func (ui *Ui) Run(cmds chan string) error {
if termbox.IsInit {
return nil
}
if err := termbox.Init(); err != nil {
return nil
}
defer termbox.Close()
termbox.SetInputMode(termbox.InputAlt)
events := make(chan termbox.Event)
go func() {
for {
events <- termbox.PollEvent()
}
}()
ui.manager.OnUiInitialize(ui)
ui.manager.OnUiDoneInitialize(ui)
width, height := termbox.Size()
ui.manager.OnUiResize(ui, width, height)
ui.Refresh()
for {
select {
case <-ui.close:
return nil
case cmd := <-cmds:
ui.onCommandEvent(cmd)
case event := <-events:
switch event.Type {
case termbox.EventResize:
ui.manager.OnUiResize(ui, event.Width, event.Height)
ui.Refresh()
case termbox.EventKey:
var k = uint32(event.Key)
if event.Ch != 0 && event.Mod != 0 {
k = uint32(event.Ch)
}
if event.Ch != 0 && event.Mod == 0 {
ui.onCharacterEvent(event.Ch)
} else {
if event.Mod > 0 {
k = k + (uint32(event.Mod) << 16)
}
ui.onKeyEvent(Key(k))
}
}
}
}
}
func (ui *Ui) onCharacterEvent(ch rune) {
if ui.activeElement != nil {
ui.activeElement.View.uiCharacterEvent(ch)
}
}
func (ui *Ui) onKeyEvent(key Key) {
if ui.keyListeners[key] != nil {
for _, listener := range ui.keyListeners[key] {
listener(ui, key)
}
}
if ui.activeElement != nil {
ui.activeElement.View.uiKeyEvent(key)
}
}
func (ui *Ui) onCommandEvent(cmd string) {
ta := strings.SplitN(cmd, " ", 2)
t := ta[0]
rest := ""
if len(ta) == 2 {
rest = ta[1]
}
if ui.commandListeners[t] != nil {
for _, listener := range ui.commandListeners[t] {
listener(ui, rest)
}
}
if ui.commandListeners["*"] != nil {
for _, listener := range ui.commandListeners["*"] {
listener(ui, cmd)
}
}
// if ui.activeElement != nil {
// ui.activeElement.View.uiKeyEvent(key)
// }
}
func (ui *Ui) Add(name string, view View) error {
if _, ok := ui.elements[name]; ok {
return errors.New("view already exists")
}
ui.elements[name] = &uiElement{
Name: name,
View: view,
}
view.uiInitialize(ui)
return nil
}
func (ui *Ui) SetBounds(name string, x0, y0, x1, y1 int) error {
element, ok := ui.elements[name]
if !ok {
return errors.New("view does not exist")
}
element.X0, element.Y0, element.X1, element.Y1 = x0, y0, x1, y1
element.View.uiSetBounds(x0, y0, x1, y1)
return nil
}
func (ui *Ui) AddKeyListener(listener KeyListener, key *Key) {
if key != nil {
ui.keyListeners[*key] = append(ui.keyListeners[*key], listener)
}
}
func (ui *Ui) AddCommandListener(listener CommandListener, cmd string) {
//if cmd!=nil {
ui.commandListeners[cmd] = append(ui.commandListeners[cmd], listener)
//}
}

11
uiterm/view.go Normal file
View File

@ -0,0 +1,11 @@
package uiterm
type View interface {
uiInitialize(ui *Ui)
uiSetActive(active bool)
uiSetBounds(x0, y0, x1, y1 int)
uiDraw()
uiKeyEvent(key Key)
uiCharacterEvent(ch rune)
// commandEvent(cmd string)
}