My mirror of the Barnard terminal client for Mumble.

163 lines
3.0 KiB

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