My mirror of the Barnard terminal client for Mumble.

165 lines
3.0 KiB

  1. package uiterm
  2. import (
  3. "errors"
  4. "github.com/nsf/termbox-go"
  5. )
  6. type LayoutFunc func(ui *Ui, width, height int)
  7. type KeyListener func(ui *Ui, key Key)
  8. type UiManager interface {
  9. OnUiInitialize(ui *Ui)
  10. OnUiResize(ui *Ui, width, height int)
  11. }
  12. type Ui struct {
  13. Fg Attribute
  14. Bg Attribute
  15. close chan bool
  16. manager UiManager
  17. elements map[string]*uiElement
  18. activeElement *uiElement
  19. keyListeners map[Key][]KeyListener
  20. }
  21. type uiElement struct {
  22. X0, Y0, X1, Y1 int
  23. View View
  24. }
  25. func New(manager UiManager) *Ui {
  26. ui := &Ui{
  27. close: make(chan bool, 10),
  28. elements: make(map[string]*uiElement),
  29. manager: manager,
  30. keyListeners: make(map[Key][]KeyListener),
  31. }
  32. return ui
  33. }
  34. func (ui *Ui) Close() {
  35. if termbox.IsInit {
  36. ui.close <- true
  37. }
  38. }
  39. func (ui *Ui) Refresh() {
  40. if termbox.IsInit {
  41. termbox.Clear(termbox.Attribute(ui.Fg), termbox.Attribute(ui.Bg))
  42. termbox.HideCursor()
  43. for _, element := range ui.elements {
  44. element.View.draw()
  45. }
  46. termbox.Flush()
  47. }
  48. }
  49. func (ui *Ui) Active() View {
  50. return ui.activeElement.View
  51. }
  52. func (ui *Ui) SetActive(name string) {
  53. element, _ := ui.elements[name]
  54. if ui.activeElement != nil {
  55. ui.activeElement.View.setActive(false)
  56. }
  57. ui.activeElement = element
  58. if element != nil {
  59. element.View.setActive(true)
  60. }
  61. ui.Refresh()
  62. }
  63. func (ui *Ui) Run() error {
  64. if termbox.IsInit {
  65. return nil
  66. }
  67. if err := termbox.Init(); err != nil {
  68. return nil
  69. }
  70. defer termbox.Close()
  71. termbox.SetInputMode(termbox.InputAlt)
  72. events := make(chan termbox.Event)
  73. go func() {
  74. for {
  75. events <- termbox.PollEvent()
  76. }
  77. }()
  78. ui.manager.OnUiInitialize(ui)
  79. width, height := termbox.Size()
  80. ui.manager.OnUiResize(ui, width, height)
  81. ui.Refresh()
  82. for {
  83. select {
  84. case <-ui.close:
  85. return nil
  86. case event := <-events:
  87. switch event.Type {
  88. case termbox.EventResize:
  89. ui.manager.OnUiResize(ui, event.Width, event.Height)
  90. ui.Refresh()
  91. case termbox.EventKey:
  92. if event.Ch != 0 {
  93. ui.onCharacterEvent(event.Ch)
  94. } else {
  95. ui.onKeyEvent(Modifier(event.Mod), Key(event.Key))
  96. }
  97. }
  98. }
  99. }
  100. }
  101. func (ui *Ui) onCharacterEvent(ch rune) {
  102. if ui.activeElement != nil {
  103. ui.activeElement.View.characterEvent(ch)
  104. }
  105. }
  106. func (ui *Ui) onKeyEvent(mod Modifier, key Key) {
  107. if ui.keyListeners[key] != nil {
  108. for _, listener := range ui.keyListeners[key] {
  109. listener(ui, key)
  110. }
  111. }
  112. if ui.activeElement != nil {
  113. ui.activeElement.View.keyEvent(mod, key)
  114. }
  115. }
  116. func (ui *Ui) Add(name string, view View) error {
  117. if _, ok := ui.elements[name]; ok {
  118. return errors.New("view already exists")
  119. }
  120. ui.elements[name] = &uiElement{
  121. View: view,
  122. }
  123. view.uiInitialize(ui)
  124. return nil
  125. }
  126. func (ui *Ui) SetBounds(name string, x0, y0, x1, y1 int) error {
  127. element, ok := ui.elements[name]
  128. if !ok {
  129. return errors.New("view cannot be found")
  130. }
  131. element.X0 = x0
  132. element.Y0 = y0
  133. element.X1 = x1
  134. element.Y1 = y1
  135. element.View.setBounds(x0, y0, x1, y1)
  136. return nil
  137. }
  138. func (ui *Ui) AddKeyListener(listener KeyListener, key Key) {
  139. ui.keyListeners[key] = append(ui.keyListeners[key], listener)
  140. }