| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 | package doorimport (	"log"	"strings"	"unicode")/*type MenuOption struct {	Ch   rune	Text string}*/type Menu struct {	Chosen      int	SelectedR   func(string) string	UnselectedR func(string) string	Options     []rune	// MenuOptions []MenuOption	Panel}func MakeMenuRender(bracketColor, optionColor, upperColor, lowerColor string) func(string) string {	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]))	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 choicefunc (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 update_and_exit {			return m.Chosen + 1		}		updated = false		var event int = d.Key()		if event < 0 {			return event		}		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			}		}		switch event {		case '8':			if use_numberpad {				goto use8			}			break		use8:			fallthrough		case XKEY_UP_ARROW:			if m.Chosen > 0 {				m.Chosen--				updated = true			}		case '2':			if use_numberpad {				goto use2			}			break		use2:			fallthrough		case XKEY_DOWN_ARROW:			if m.Chosen < len(m.Lines)-1 {				m.Chosen++				updated = true			}		case XKEY_HOME:			if m.Chosen != 0 {				m.Chosen = 0				updated = true			}		case XKEY_END:			if m.Chosen != len(m.Lines)-1 {				m.Chosen = len(m.Lines) - 1				updated = true			}		case 0x0d:			// use current selection			return m.Chosen + 1		default:			// Is the key in the list of options?			if event < 0x1000 {				// 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(rune(event)) {						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		}	}}
 |