]> git.0d.be Git - barnard.git/blob - uiterm/tree.go
5cdc618d7665cb69e45d411c81ac463e822270c7
[barnard.git] / uiterm / tree.go
1 package uiterm // import "layeh.com/barnard/uiterm"
2
3 import (
4         "strings"
5
6         "github.com/nsf/termbox-go"
7 )
8
9 type TreeItem interface {
10         TreeItemStyle(fg, bg Attribute, active bool) (Attribute, Attribute)
11         String() string
12 }
13
14 type renderedTreeItem struct {
15         //String string
16         Level int
17         Item  TreeItem
18 }
19
20 type Tree struct {
21         Fg, Bg    Attribute
22         Generator func(item TreeItem) []TreeItem
23         Listener  func(ui *Ui, tree *Tree, item TreeItem)
24
25         lines      []renderedTreeItem
26         activeLine int
27
28         ui             *Ui
29         active         bool
30         x0, y0, x1, y1 int
31 }
32
33 func bounded(i, lower, upper int) int {
34         if i < lower {
35                 return lower
36         }
37         if i > upper {
38                 return upper
39         }
40         return i
41 }
42
43 func (t *Tree) uiInitialize(ui *Ui) {
44         t.ui = ui
45 }
46
47 func (t *Tree) uiSetActive(active bool) {
48         t.active = active
49         t.uiDraw()
50 }
51
52 func (t *Tree) uiSetBounds(x0, y0, x1, y1 int) {
53         t.x0 = x0
54         t.y0 = y0
55         t.x1 = x1
56         t.y1 = y1
57         t.uiDraw()
58 }
59
60 func (t *Tree) Rebuild() {
61         if t.Generator == nil {
62                 t.lines = []renderedTreeItem{}
63                 return
64         }
65
66         lines := []renderedTreeItem{}
67         for _, item := range t.Generator(nil) {
68                 children := t.rebuild_rec(item, 0)
69                 if children != nil {
70                         lines = append(lines, children...)
71                 }
72         }
73         t.lines = lines
74         t.activeLine = bounded(t.activeLine, 0, len(t.lines)-1)
75         t.uiDraw()
76 }
77
78 func (t *Tree) rebuild_rec(parent TreeItem, level int) []renderedTreeItem {
79         if parent == nil {
80                 return nil
81         }
82         lines := []renderedTreeItem{
83                 renderedTreeItem{
84                         Level: level,
85                         Item:  parent,
86                 },
87         }
88         for _, item := range t.Generator(parent) {
89                 children := t.rebuild_rec(item, level+1)
90                 if children != nil {
91                         lines = append(lines, children...)
92                 }
93         }
94         return lines
95 }
96
97 func (t *Tree) uiDraw() {
98         t.ui.beginDraw()
99         defer t.ui.endDraw()
100
101         if t.lines == nil {
102                 t.Rebuild()
103         }
104
105         line := 0
106         for y := t.y0; y < t.y1; y++ {
107                 var reader *strings.Reader
108                 var item TreeItem
109                 level := 0
110                 if line < len(t.lines) {
111                         item = t.lines[line].Item
112                         level = t.lines[line].Level
113                         reader = strings.NewReader(item.String())
114                 }
115                 for x := t.x0; x < t.x1; x++ {
116                         var chr rune = ' '
117                         fg := t.Fg
118                         bg := t.Bg
119                         dx := x - t.x0
120                         dy := y - t.y0
121                         if reader != nil && level*2 <= dx {
122                                 if ch, _, err := reader.ReadRune(); err == nil {
123                                         chr = ch
124                                         fg, bg = item.TreeItemStyle(fg, bg, t.active && t.activeLine == dy)
125                                 }
126                         }
127                         termbox.SetCell(x, y, chr, termbox.Attribute(fg), termbox.Attribute(bg))
128                 }
129                 line++
130         }
131 }
132
133 func (t *Tree) uiKeyEvent(mod Modifier, key Key) {
134         switch key {
135         case KeyArrowUp:
136                 t.activeLine = bounded(t.activeLine-1, 0, len(t.lines)-1)
137         case KeyArrowDown:
138                 t.activeLine = bounded(t.activeLine+1, 0, len(t.lines)-1)
139         case KeyEnter:
140                 if t.Listener != nil && t.activeLine >= 0 && t.activeLine < len(t.lines) {
141                         t.Listener(t.ui, t, t.lines[t.activeLine].Item)
142                 }
143         }
144         t.uiDraw()
145 }
146
147 func (t *Tree) uiCharacterEvent(ch rune) {
148 }