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) setBounds(x0, y0, x1, y1 int) {
  41. t.x0 = x0
  42. t.y0 = y0
  43. t.x1 = x1
  44. t.y1 = y1
  45. }
  46. func (t *Tree) Rebuild() {
  47. if t.Generator == nil {
  48. t.lines = []renderedTreeItem{}
  49. return
  50. }
  51. lines := []renderedTreeItem{}
  52. for _, item := range t.Generator(nil) {
  53. children := t.rebuild_rec(item, 0)
  54. if children != nil {
  55. lines = append(lines, children...)
  56. }
  57. }
  58. t.lines = lines
  59. t.activeLine = bounded(t.activeLine, 0, len(t.lines)-1)
  60. }
  61. func (t *Tree) rebuild_rec(parent TreeItem, level int) []renderedTreeItem {
  62. if parent == nil {
  63. return nil
  64. }
  65. lines := []renderedTreeItem{
  66. renderedTreeItem{
  67. Level: level,
  68. Item: parent,
  69. },
  70. }
  71. for _, item := range t.Generator(parent) {
  72. children := t.rebuild_rec(item, level+1)
  73. if children != nil {
  74. lines = append(lines, children...)
  75. }
  76. }
  77. return lines
  78. }
  79. func (t *Tree) draw() {
  80. if t.lines == nil {
  81. t.Rebuild()
  82. }
  83. line := 0
  84. for y := t.y0; y < t.y1; y++ {
  85. var reader *strings.Reader
  86. var item TreeItem
  87. level := 0
  88. if line < len(t.lines) {
  89. item = t.lines[line].Item
  90. level = t.lines[line].Level
  91. reader = strings.NewReader(item.String())
  92. }
  93. for x := t.x0; x < t.x1; x++ {
  94. var chr rune = ' '
  95. fg := t.Fg
  96. bg := t.Bg
  97. dx := x - t.x0
  98. dy := y - t.y0
  99. if reader != nil && level*2 <= dx {
  100. if ch, _, err := reader.ReadRune(); err == nil {
  101. chr = ch
  102. fg, bg = item.TreeItemStyle(t.active && t.activeLine == dy)
  103. }
  104. }
  105. termbox.SetCell(x, y, chr, termbox.Attribute(fg), termbox.Attribute(bg))
  106. }
  107. line++
  108. }
  109. }
  110. func (t *Tree) setActive(active bool) {
  111. t.active = active
  112. }
  113. func (t *Tree) keyEvent(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) characterEvent(ch rune) {
  127. }