package door import ( "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 Activated func(int, *Door) // Which option is active // 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 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) } } 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 { log.Println("Mouse (", mouse.X, ",", mouse.Y, ")") } } } 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 } } }