menu.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package door
  2. import (
  3. "strings"
  4. "unicode"
  5. )
  6. type MenuOption struct {
  7. Ch rune
  8. Text string
  9. }
  10. type Menu struct {
  11. Chosen int
  12. SelectedR func(string) string
  13. UnselectedR func(string) string
  14. Options []rune
  15. MenuOptions []MenuOption
  16. Panel
  17. }
  18. func MakeMenuRender(c1, c2, c3, c4 string) func(string) string {
  19. f := func(text string) string {
  20. var output string
  21. var lastColor string
  22. option := true
  23. for _, c := range text {
  24. if option {
  25. if c == '[' || c == ']' {
  26. if lastColor != c1 {
  27. output += c1
  28. lastColor = c1
  29. }
  30. output += string(c)
  31. option = (c == '[')
  32. } else {
  33. if lastColor != c2 {
  34. output += c2
  35. lastColor = c2
  36. }
  37. output += string(c)
  38. }
  39. } else {
  40. if unicode.IsUpper(c) {
  41. if lastColor != c3 {
  42. output += c3
  43. lastColor = c3
  44. }
  45. output += string(c)
  46. } else {
  47. if lastColor != c4 {
  48. output += c4
  49. lastColor = c4
  50. }
  51. output += string(c)
  52. }
  53. }
  54. }
  55. return output
  56. }
  57. return f
  58. }
  59. // Should I be using this, or write a function like the original --
  60. // m.AddSelection("K", "Text") to build? (And throw away the
  61. // redundant) MenuOptions/MenuOption parts? They do let me create
  62. // the menu in larger chunks -- but it is redundant.
  63. // Once the menu is built, I really could delete the MenuOptions.
  64. func (m *Menu) Build() {
  65. // Take MenuOptions and build the Menu
  66. // Reset
  67. m.Lines = make([]Line, 0)
  68. m.Options = make([]rune, 0)
  69. m.Chosen = 0
  70. for _, option := range m.MenuOptions {
  71. m.Options = append(m.Options, option.Ch)
  72. text := "[" + string(option.Ch) + "] " + option.Text + strings.Repeat(" ", m.Width-(4+len(option.Text)))
  73. m.Lines = append(m.Lines, Line{Text: text, RenderF: m.UnselectedR})
  74. }
  75. }
  76. func (m *Menu) Choose(d *Door) int {
  77. var changed []int
  78. updated := true
  79. update_and_exit := false
  80. blank := ColorText("BLACK")
  81. for {
  82. if updated {
  83. for x := range m.Lines {
  84. if x == m.Chosen {
  85. m.Lines[x].RenderF = m.SelectedR
  86. } else {
  87. m.Lines[x].RenderF = m.UnselectedR
  88. }
  89. }
  90. if len(changed) == 0 {
  91. d.Write(m.Output() + blank)
  92. } else {
  93. // Update just the lines that have changed
  94. for _, line := range changed {
  95. d.Write(m.UpdateLine(line))
  96. }
  97. d.Write(m.GotoEnd() + blank)
  98. }
  99. }
  100. if update_and_exit {
  101. return m.Chosen + 1
  102. }
  103. updated = false
  104. var event int = d.Key()
  105. if event < 0 {
  106. return event
  107. }
  108. previous_choice := m.Chosen
  109. changed = make([]int, 0)
  110. use_numberpad := true
  111. for _, option := range m.MenuOptions {
  112. if option.Ch == rune('8') || option.Ch == rune('2') {
  113. use_numberpad = false
  114. }
  115. }
  116. switch event {
  117. case '8':
  118. if use_numberpad {
  119. goto use8
  120. }
  121. break
  122. use8:
  123. fallthrough
  124. case XKEY_UP_ARROW:
  125. if m.Chosen > 0 {
  126. m.Chosen--
  127. updated = true
  128. }
  129. case '2':
  130. if use_numberpad {
  131. goto use2
  132. }
  133. break
  134. use2:
  135. fallthrough
  136. case XKEY_DOWN_ARROW:
  137. if m.Chosen < len(m.Lines)-1 {
  138. m.Chosen++
  139. updated = true
  140. }
  141. case XKEY_HOME:
  142. if m.Chosen != 0 {
  143. m.Chosen = 0
  144. updated = true
  145. }
  146. case XKEY_END:
  147. if m.Chosen != len(m.Lines)-1 {
  148. m.Chosen = len(m.Lines) - 1
  149. updated = true
  150. }
  151. case 0x0d:
  152. // use current selection
  153. return m.Chosen + 1
  154. default:
  155. // Is the key in the list of options?
  156. if event < 0x1000 {
  157. for x, option := range m.MenuOptions {
  158. if unicode.ToUpper(rune(option.Ch)) == unicode.ToUpper(rune(event)) {
  159. if m.Chosen == x {
  160. return x + 1
  161. }
  162. updated = true
  163. m.Chosen = x
  164. update_and_exit = true
  165. }
  166. }
  167. }
  168. }
  169. if previous_choice != m.Chosen {
  170. changed = append(changed, previous_choice)
  171. changed = append(changed, m.Chosen)
  172. updated = true
  173. }
  174. }
  175. }