My mirror of the Barnard terminal client for Mumble.

147 lines
2.7 KiB

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