From da19f61603efef6944f4d9e769bb3d27e7caad21 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Mon, 26 Oct 2015 07:39:18 -0500 Subject: [PATCH] Added ASCIIArt object and Menu Object --- termbox_asciiart.go | 85 +++++++++++++++++ termbox_menu.go | 222 ++++++++++++++++++++++++++++++++++++++++++++ termbox_util.go | 12 +-- 3 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 termbox_asciiart.go create mode 100644 termbox_menu.go diff --git a/termbox_asciiart.go b/termbox_asciiart.go new file mode 100644 index 0000000..9b60d11 --- /dev/null +++ b/termbox_asciiart.go @@ -0,0 +1,85 @@ +package termboxUtil + +import ( + "github.com/nsf/termbox-go" +) + +// ASCIIArt is a []string with more functions +type ASCIIArt struct { + contents []string + x, y int + bg, fg termbox.Attribute +} + +// CreateASCIIArt Create an ASCII art object from a string slice +func CreateASCIIArt(c []string, x, y int, fg, bg termbox.Attribute) *ASCIIArt { + i := ASCIIArt{contents: c, x: x, y: y, fg: fg, bg: bg} + return &i +} + +// GetX Return the x position of the modal +func (i *ASCIIArt) GetX() int { return i.x } + +// SetX set the x position of the modal to x +func (i *ASCIIArt) SetX(x int) *ASCIIArt { + i.x = x + return i +} + +// GetY Return the y position of the modal +func (i *ASCIIArt) GetY() int { return i.y } + +// SetY Set the y position of the modal to y +func (i *ASCIIArt) SetY(y int) *ASCIIArt { + i.y = y + return i +} + +// GetBackground Return the current background color of the modal +func (i *ASCIIArt) GetBackground() termbox.Attribute { return i.bg } + +// SetBackground Set the current background color to bg +func (i *ASCIIArt) SetBackground(bg termbox.Attribute) *ASCIIArt { + i.bg = bg + return i +} + +// GetForeground Return the current foreground color +func (i *ASCIIArt) GetForeground() termbox.Attribute { return i.fg } + +// SetForeground Set the foreground color to fg +func (i *ASCIIArt) SetForeground(fg termbox.Attribute) *ASCIIArt { + i.fg = fg + return i +} + +// Align Align the Ascii art over width width with alignment a +func (i *ASCIIArt) Align(a TextAlignment, width int) *ASCIIArt { + // First get the width of the longest string in the slice + var newContents []string + incomingLength := 0 + for _, line := range i.contents { + if len(line) > incomingLength { + incomingLength = len(line) + } + } + for _, line := range i.contents { + newContents = append(newContents, AlignText(AlignText(line, incomingLength, AlignLeft), width, a)) + } + i.contents = newContents + return i +} + +// HandleKeyPress accepts the termbox event and returns whether it was consumed +func (i *ASCIIArt) HandleKeyPress(event termbox.Event) bool { + return false +} + +// Draw outputs the input field on the screen +func (i *ASCIIArt) Draw() { + drawX, drawY := i.x, i.y + for _, line := range i.contents { + DrawStringAtPoint(line, drawX, drawY, i.fg, i.bg) + drawY++ + } +} diff --git a/termbox_menu.go b/termbox_menu.go new file mode 100644 index 0000000..0c54a1c --- /dev/null +++ b/termbox_menu.go @@ -0,0 +1,222 @@ +package termboxUtil + +import ( + "github.com/nsf/termbox-go" +) + +// Menu is a menu with a list of options +type Menu struct { + title string + options []string + // If height is -1, then it is adaptive to the menu + x, y, width, height int + optionsDisabled []bool + optionsHelp []string + showHelp bool + cursor int + bg, fg termbox.Attribute + selectedBg, selectedFg termbox.Attribute + disabledBg, disabledFg termbox.Attribute + isDone bool + selectedOption int + bordered bool + hasFocus 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, options: options, x: x, y: y, width: width, height: height, fg: fg, bg: bg} + for len(i.optionsDisabled) < len(i.options) { + i.optionsDisabled = append(i.optionsDisabled, false) + } + i.selectedFg = i.bg + i.selectedBg = i.fg + i.disabledFg = i.bg + i.disabledBg = i.bg + 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() []string { + return i.options +} + +// SetOptions set the menu's options to opts +func (i *Menu) SetOptions(opts []string) *Menu { + i.options = opts + return i +} + +// SetOptionDisabled sets an option in the menu to disabled +func (i *Menu) SetOptionDisabled(idx int) *Menu { + if idx > 0 && idx < len(i.options) { + i.optionsDisabled[idx] = true + } + return i +} + +// SetOptionEnabled sets an option to enabled +func (i *Menu) SetOptionEnabled(idx int) *Menu { + if idx >= 0 && idx < len(i.options) { + i.optionsDisabled[idx] = false + } + return i +} + +// 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 +} + +// 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 i.hasFocus { + if event.Key == termbox.KeyEnter { + i.isDone = true + return true + } + switch event.Key { + case termbox.KeyArrowUp: + if i.selectedOption > 0 { + i.selectedOption-- + return true + } + case termbox.KeyArrowDown: + if i.selectedOption < len(i.options) { + i.selectedOption++ + 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 - 2 + } + + // 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 + for idx, opt := range i.options { + if i.optionsDisabled[idx] { + DrawStringAtPoint(opt, optionStartX, optionStartY, i.disabledFg, i.disabledBg) + } else if i.selectedOption == idx { + DrawStringAtPoint(opt, optionStartX, optionStartY, i.selectedFg, i.selectedBg) + } else { + DrawStringAtPoint(opt, optionStartX, optionStartY, i.fg, i.bg) + } + } +} diff --git a/termbox_util.go b/termbox_util.go index fd69c31..67ecb3f 100644 --- a/termbox_util.go +++ b/termbox_util.go @@ -42,16 +42,16 @@ func FillWithChar(r rune, x1, y1, x2, y2 int, fg termbox.Attribute, bg termbox.A // DrawBorder Draw a border around the area inside x1,y1 -> x2, y2 func DrawBorder(x1, y1, x2, y2 int, fg termbox.Attribute, bg termbox.Attribute) { - termbox.SetCell(x1, y1, '┌', fg, bg) - FillWithChar('─', x1+1, y1, x2-1, y1, fg, bg) - termbox.SetCell(x2, y1, '┐', fg, bg) + termbox.SetCell(x1, y1, '+', fg, bg) + FillWithChar('-', x1+1, y1, x2-1, y1, fg, bg) + termbox.SetCell(x2, y1, '+', fg, bg) FillWithChar('|', x1, y1+1, x1, y2-1, fg, bg) FillWithChar('|', x2, y1+1, x2, y2-1, fg, bg) - termbox.SetCell(x1, y2, '└', fg, bg) - FillWithChar('─', x1+1, y2, x2-1, y2, fg, bg) - termbox.SetCell(x2, y2, '┘', fg, bg) + termbox.SetCell(x1, y2, '+', fg, bg) + FillWithChar('-', x1+1, y2, x2-1, y2, fg, bg) + termbox.SetCell(x2, y2, '+', fg, bg) } // AlignText Aligns the text txt within width characters using the specified alignment