menu.go 4.2 KB

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