Initial commit, lots of cleanup and stuff to do, it may not work.
This commit is contained in:
185
uiterm/tree.go
Normal file
185
uiterm/tree.go
Normal 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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user