termbox-util/termbox_menu.go

361 lines
8.8 KiB
Go

package termboxUtil
import "github.com/nsf/termbox-go"
// Menu is a menu with a list of options
type Menu struct {
title string
options []MenuOption
// If height is -1, then it is adaptive to the menu
x, y, width, height int
showHelp bool
cursor int
bg, fg termbox.Attribute
selectedBg, selectedFg termbox.Attribute
disabledBg, disabledFg termbox.Attribute
isDone bool
bordered bool
}
// CreateMenu Creates a menu with the specified attributes
func CreateMenu(title string, options []string, x, y, width, height int, fg, bg termbox.Attribute) *Menu {
i := Menu{
title: title,
x: x, y: y, width: width, height: height,
fg: fg, bg: bg, selectedFg: bg, selectedBg: fg,
disabledFg: bg, disabledBg: bg,
}
for _, line := range options {
i.options = append(i.options, MenuOption{text: line})
}
if len(i.options) > 0 {
i.SetSelectedOption(&i.options[0])
}
return &i
}
// GetTitle returns the current title of the menu
func (i *Menu) GetTitle() string { return i.title }
// SetTitle sets the current title of the menu to s
func (i *Menu) SetTitle(s string) *Menu {
i.title = s
return i
}
// GetOptions returns the current options of the menu
func (i *Menu) GetOptions() []MenuOption {
return i.options
}
// SetOptions set the menu's options to opts
func (i *Menu) SetOptions(opts []MenuOption) *Menu {
i.options = opts
return i
}
// SetOptionsFromStrings sets the options of this menu from a slice of strings
func (i *Menu) SetOptionsFromStrings(opts []string) *Menu {
var newOpts []MenuOption
for _, v := range opts {
newOpts = append(newOpts, *CreateOptionFromText(v))
}
return i.SetOptions(newOpts)
}
// GetX returns the current x coordinate of the menu
func (i *Menu) GetX() int { return i.x }
// SetX sets the current x coordinate of the menu to x
func (i *Menu) SetX(x int) *Menu {
i.x = x
return i
}
// GetY returns the current y coordinate of the menu
func (i *Menu) GetY() int { return i.y }
// SetY sets the current y coordinate of the menu to y
func (i *Menu) SetY(y int) *Menu {
i.y = y
return i
}
// GetWidth returns the current width of the menu
func (i *Menu) GetWidth() int { return i.width }
// SetWidth sets the current menu width to width
func (i *Menu) SetWidth(width int) *Menu {
i.width = width
return i
}
// GetHeight returns the current height of the menu
func (i *Menu) GetHeight() int { return i.height }
// SetHeight set the height of the menu to height
func (i *Menu) SetHeight(height int) *Menu {
i.height = height
return i
}
// GetSelectedOption returns the current selected option
func (i *Menu) GetSelectedOption() *MenuOption {
idx := i.GetSelectedIndex()
if idx != -1 {
return &i.options[idx]
}
return nil
}
// GetOptionFromIndex Returns the
func (i *Menu) GetOptionFromIndex(idx int) *MenuOption {
if idx >= 0 && idx < len(i.options) {
return &i.options[idx]
}
return nil
}
// GetOptionFromText Returns the first option with the text v
func (i *Menu) GetOptionFromText(v string) *MenuOption {
for idx := range i.options {
testOption := &i.options[idx]
if testOption.GetText() == v {
return testOption
}
}
return nil
}
// GetSelectedIndex returns the index of the selected option
// Returns -1 if nothing is selected
func (i *Menu) GetSelectedIndex() int {
for idx := range i.options {
if i.options[idx].IsSelected() {
return idx
}
}
return -1
}
// SetSelectedOption sets the current selected option to v (if it's valid)
func (i *Menu) SetSelectedOption(v *MenuOption) *Menu {
for idx := range i.options {
if &i.options[idx] == v {
i.options[idx].Select()
} else {
i.options[idx].Unselect()
}
}
return i
}
// SelectPrevOption Decrements the selected option (if it can)
func (i *Menu) SelectPrevOption() *Menu {
idx := i.GetSelectedIndex()
for idx >= 0 {
idx--
testOption := i.GetOptionFromIndex(idx)
if testOption != nil && !testOption.IsDisabled() {
i.SetSelectedOption(testOption)
return i
}
}
return i
}
// SelectNextOption Increments the selected option (if it can)
func (i *Menu) SelectNextOption() *Menu {
idx := i.GetSelectedIndex()
for idx < len(i.options) {
idx++
testOption := i.GetOptionFromIndex(idx)
if testOption != nil && !testOption.IsDisabled() {
i.SetSelectedOption(testOption)
return i
}
}
return i
}
// HelpIsShown returns true or false if the help is displayed
func (i *Menu) HelpIsShown() bool { return i.showHelp }
// ShowHelp sets whether or not to display the help text
func (i *Menu) ShowHelp(b bool) *Menu {
i.showHelp = b
return i
}
// GetBackground returns the current background color
func (i *Menu) GetBackground() termbox.Attribute { return i.bg }
// SetBackground sets the background color to bg
func (i *Menu) SetBackground(bg termbox.Attribute) *Menu {
i.bg = bg
return i
}
// GetForeground returns the current foreground color
func (i *Menu) GetForeground() termbox.Attribute { return i.fg }
// SetForeground sets the current foreground color to fg
func (i *Menu) SetForeground(fg termbox.Attribute) *Menu {
i.fg = fg
return i
}
// IsDone returns whether the user has answered the modal
func (i *Menu) IsDone() bool { return i.isDone }
// SetDone sets whether the modal has completed it's purpose
func (i *Menu) SetDone(b bool) *Menu {
i.isDone = b
return i
}
// IsBordered returns true or false if this menu has a border
func (i *Menu) IsBordered() bool { return i.bordered }
// SetBordered sets whether we render a border around the menu
func (i *Menu) SetBordered(b bool) *Menu {
i.bordered = b
return i
}
// HandleKeyPress handles the termbox event and returns whether it was consumed
func (i *Menu) HandleKeyPress(event termbox.Event) bool {
if event.Key == termbox.KeyEnter {
i.isDone = true
return true
}
currentIdx := i.GetSelectedIndex()
switch event.Key {
case termbox.KeyArrowUp:
i.SelectPrevOption()
case termbox.KeyArrowDown:
i.SelectNextOption()
}
if i.GetSelectedIndex() != currentIdx {
return true
}
return false
}
// Draw draws the modal
func (i *Menu) Draw() {
// First blank out the area we'll be putting the menu
FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
// Now draw the border
optionStartX := i.x
optionStartY := i.y
optionWidth := i.width
optionHeight := i.height
if optionHeight == -1 {
optionHeight = len(i.options)
}
if i.bordered {
if i.height == -1 {
DrawBorder(i.x, i.y, i.x+i.width, i.y+optionHeight+1, i.fg, i.bg)
} else {
DrawBorder(i.x, i.y, i.x+i.width, i.y+optionHeight, i.fg, i.bg)
}
optionStartX = i.x + 1
optionStartY = i.y + 1
optionWidth = i.width - 1
}
// The title
if i.title != "" {
DrawStringAtPoint(AlignText(i.title, optionWidth, AlignCenter), optionStartX, optionStartY, i.fg, i.bg)
optionStartY++
if i.bordered {
FillWithChar('-', optionStartX, optionStartY, optionWidth, optionStartY, i.fg, i.bg)
optionStartY++
}
}
// Print the options
if len(i.options) > 0 {
for idx := range i.options {
currOpt := &i.options[idx]
if currOpt.IsDisabled() {
DrawStringAtPoint(currOpt.GetText(), optionStartX, optionStartY, i.disabledFg, i.disabledBg)
} else if i.GetSelectedOption() == currOpt {
DrawStringAtPoint(AlignText(currOpt.GetText(), optionWidth, AlignLeft), optionStartX, optionStartY, i.selectedFg, i.selectedBg)
} else {
DrawStringAtPoint(currOpt.GetText(), optionStartX, optionStartY, i.fg, i.bg)
}
optionStartY++
}
}
}
/* MenuOption Struct & methods */
// MenuOption An option in the menu
type MenuOption struct {
text string
selected bool
disabled bool
helpText string
}
// CreateOptionFromText just returns a MenuOption object
// That only has it's text value set.
func CreateOptionFromText(s string) *MenuOption {
return &MenuOption{text: s}
}
// SetText Sets the text for this option
func (i *MenuOption) SetText(s string) *MenuOption {
i.text = s
return i
}
// GetText Returns the text for this option
func (i *MenuOption) GetText() string { return i.text }
// Disable Sets this option to disabled
func (i *MenuOption) Disable() *MenuOption {
i.disabled = true
return i
}
// Enable Sets this option to enabled
func (i *MenuOption) Enable() *MenuOption {
i.disabled = false
return i
}
// IsDisabled returns whether this option is enabled
func (i *MenuOption) IsDisabled() bool {
return i.disabled
}
// IsSelected Returns whether this option is selected
func (i *MenuOption) IsSelected() bool {
return i.selected
}
// Select Sets this option to selected
func (i *MenuOption) Select() *MenuOption {
i.selected = true
return i
}
// Unselect Sets this option to not selected
func (i *MenuOption) Unselect() *MenuOption {
i.selected = false
return i
}
// SetHelpText Sets this option's help text to s
func (i *MenuOption) SetHelpText(s string) *MenuOption {
i.helpText = s
return i
}
// GetHelpText Returns the help text for this option
func (i *MenuOption) GetHelpText() string { return i.helpText }