menu.go 4.4 KB

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