123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- package door
- import (
- "log"
- "strings"
- "unicode"
- )
- /*
- type MenuOption struct {
- Ch rune
- Text string
- }
- */
- type Menu struct {
- Chosen int
- SelectedR ColorRender
- UnselectedR ColorRender
- Options []rune
- Activated func(int, *Door) // Which option is active
- // MenuOptions []MenuOption
- Panel
- }
- func MakeMenuRender(bracketColor, optionColor, upperColor, lowerColor string) ColorRender {
- f := func(text string) string {
- var output string
- var lastColor string
- option := true
- for _, c := range text {
- if option {
- if c == '[' || c == ']' {
- if lastColor != bracketColor {
- output += bracketColor
- lastColor = bracketColor
- }
- output += string(c)
- option = (c == '[')
- } else {
- if lastColor != optionColor {
- output += optionColor
- lastColor = optionColor
- }
- output += string(c)
- }
- } else {
- if unicode.IsUpper(c) {
- if lastColor != upperColor {
- output += upperColor
- lastColor = upperColor
- }
- output += string(c)
- } else {
- if lastColor != lowerColor {
- output += lowerColor
- lastColor = lowerColor
- }
- output += string(c)
- }
- }
- }
- return output
- }
- return f
- }
- func (m *Menu) AddSelection(key string, text string) {
- key = key[:1] // Make sure it is just 1 character
- m.Options = append(m.Options, rune(key[0]))
- // 4 = [ key ] + " "
- if len(text)+4 > m.Width {
- log.Panicf("Menu (not wide enough) Width %d : text size %d + 4 = %d\n", m.Width, len(text), len(text)+4)
- }
- linetext := "[" + key + "] " + text + strings.Repeat(" ", m.Width-(4+len(text)))
- m.Lines = append(m.Lines, Line{Text: linetext, RenderF: m.UnselectedR})
- }
- // Should I be using this, or write a function like the original --
- // m.AddSelection("K", "Text") to build? (And throw away the
- // redundant) MenuOptions/MenuOption parts? They do let me create
- // the menu in larger chunks -- but it is redundant.
- // Once the menu is built, I really could delete the MenuOptions.
- /*
- func (m *Menu) Build() {
- // Take MenuOptions and build the Menu
- // Reset
- m.Lines = make([]Line, 0)
- m.Options = make([]rune, 0)
- m.Chosen = 0
- for _, option := range m.MenuOptions {
- m.Options = append(m.Options, option.Ch)
- text := "[" + string(option.Ch) + "] " + option.Text + strings.Repeat(" ", m.Width-(4+len(option.Text)))
- m.Lines = append(m.Lines, Line{Text: text, RenderF: m.UnselectedR})
- }
- }
- */
- // Get the character that was pressed from the choice
- func (m *Menu) GetOption(choice int) rune {
- return m.Options[choice-1]
- }
- func (m *Menu) Choose(d *Door) int {
- var changed []int
- updated := true
- update_and_exit := false
- blank := ColorText("BLACK")
- for {
- if updated {
- for x := range m.Lines {
- if x == m.Chosen {
- m.Lines[x].RenderF = m.SelectedR
- } else {
- m.Lines[x].RenderF = m.UnselectedR
- }
- }
- if len(changed) == 0 {
- d.Write(m.Output() + blank)
- } else {
- // Update just the lines that have changed
- for _, line := range changed {
- d.Write(m.UpdateLine(line))
- }
- d.Write(m.GotoEnd() + blank)
- }
- if m.Activated != nil {
- m.Activated(m.Chosen, d)
- // Reposition to the end of the menu
- d.Write(m.GotoEnd() + blank)
- }
- }
- if update_and_exit {
- return m.Chosen + 1
- }
- updated = false
- var r rune
- var ex Extended
- var err error
- r, ex, err = d.WaitKey(DefaultTimeout)
- if err != nil {
- return -1
- }
- previous_choice := m.Chosen
- changed = make([]int, 0)
- use_numberpad := true
- for _, option := range m.Options {
- if option == rune('8') || option == rune('2') {
- use_numberpad = false
- }
- }
- if ex == MOUSE {
- mouse, ok := d.GetMouse()
- if ok {
- if mouse.Button == 65 {
- // Translate Mouse Wheel Up
- ex = UP_ARROW
- }
- if mouse.Button == 66 {
- // Translate Mouse Wheel Down
- ex = DOWN_ARROW
- }
- // Look at Mouse X/Y to determine where they clicked
- if mouse.Button == 1 || mouse.Button == 2 {
- // TODO: log.Println("Mouse (", mouse.X, ",", mouse.Y, ")")
- clicked, cx, cy := m.Panel.Within(int(mouse.X), int(mouse.Y))
- log.Printf("%t : (%d, %d)\n", clicked, cx, cy)
- if clicked {
- if m.Panel.HasBorder() {
- // Did they click the border?
- if cy > 0 && cy <= m.Panel.Length() {
- updated = true
- m.Chosen = cy - 1
- update_and_exit = true
- }
- } else {
- // Borderless -- yes, they clicked something!
- updated = true
- m.Chosen = cy
- update_and_exit = true
- }
- }
- }
- }
- }
- switch ex {
- case UP_ARROW:
- if m.Chosen > 0 {
- m.Chosen--
- updated = true
- }
- case DOWN_ARROW:
- if m.Chosen < len(m.Lines)-1 {
- m.Chosen++
- updated = true
- }
- case HOME:
- if m.Chosen != 0 {
- m.Chosen = 0
- updated = true
- }
- case END:
- if m.Chosen != len(m.Lines)-1 {
- m.Chosen = len(m.Lines) - 1
- updated = true
- }
- }
- switch r {
- case '8':
- if use_numberpad {
- if m.Chosen > 0 {
- m.Chosen--
- updated = true
- }
- }
- case '2':
- if use_numberpad {
- if m.Chosen < len(m.Lines)-1 {
- m.Chosen++
- updated = true
- }
- }
- case 0x0d:
- // use current selection
- return m.Chosen + 1
- default:
- // Is the key in the list of options?
- // fmt.Printf("Event: %d\n", event)
- for x, option := range m.Options {
- // fmt.Printf("Checking %d, %d\n", x, option)
- if unicode.ToUpper(option) == unicode.ToUpper(r) {
- if m.Chosen == x {
- return x + 1
- }
- updated = true
- m.Chosen = x
- update_and_exit = true
- }
- }
- }
- if previous_choice != m.Chosen {
- changed = append(changed, previous_choice)
- changed = append(changed, m.Chosen)
- updated = true
- }
- }
- }
|