Just some stuff

This commit is contained in:
Brian Buller 2019-03-25 10:10:25 -05:00
parent 80b1bdea74
commit c168c0df31
14 changed files with 1072 additions and 766 deletions

View File

@ -5,7 +5,7 @@ import (
"strconv"
"syscall"
"gogs.bullercodeworks.com/brian/termbox-util"
"github.com/br0xen/termbox-util"
"github.com/nsf/termbox-go"
)

View File

@ -13,17 +13,20 @@ type AlertModal struct {
showHelp bool
cursor int
bg, fg termbox.Attribute
activeFg, activeBg termbox.Attribute
isDone bool
accepted bool
value string
isVisible bool
bordered bool
tabSkip bool
active bool
}
// CreateAlertModal Creates a confirmation modal with the specified attributes
func CreateAlertModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *AlertModal {
i := AlertModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, bordered: true}
i.activeFg, i.activeBg = fg, bg
if i.title == "" {
i.title = "Alert!"
}
@ -31,6 +34,11 @@ func CreateAlertModal(title string, x, y, width, height int, fg, bg termbox.Attr
return &i
}
func (i *AlertModal) SetActiveFgColor(fg termbox.Attribute) { i.activeFg = fg }
func (i *AlertModal) SetActiveBgColor(bg termbox.Attribute) { i.activeBg = bg }
func (i *AlertModal) SetActive(a bool) { i.active = a }
func (i *AlertModal) IsActive() bool { return i.active }
// GetID returns this control's ID
func (i *AlertModal) GetID() string { return i.id }

View File

@ -8,17 +8,20 @@ import (
// ASCIIArt is a []string with more functions
type ASCIIArt struct {
id string
contents []string
x, y int
bg, fg termbox.Attribute
bordered bool
tabSkip bool
id string
contents []string
x, y int
bg, fg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool
tabSkip bool
active bool
}
// 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, bordered: false, tabSkip: true}
i.activeFg, i.activeBg = fg, bg
return &i
}
@ -87,6 +90,11 @@ func (i *ASCIIArt) SetWidth(w int) {
}
}
func (i *ASCIIArt) SetActiveFgColor(fg termbox.Attribute) { i.activeFg = fg }
func (i *ASCIIArt) SetActiveBgColor(bg termbox.Attribute) { i.activeBg = bg }
func (i *ASCIIArt) SetActive(a bool) { i.active = a }
func (i *ASCIIArt) IsActive() bool { return i.active }
// SetContents Sets the contents of i to c
func (i *ASCIIArt) SetContents(c []string) {
i.contents = c

59
termbox_button.go Normal file
View File

@ -0,0 +1,59 @@
package termboxUtil
import termbox "github.com/nsf/termbox-go"
type Button struct {
id string
x, y, width, height int
label string
fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool
tabSkip bool
active bool
}
func CreateButton(x, y, w, h int, fg, bg termbox.Attribute) *Button {
c := Button{
x: x, y: y, width: w, height: h,
fg: fg, bg: bg, activeFg: bg, activeBg: fg,
bordered: true,
tabSkip: true,
}
return &c
}
func (c *Button) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg }
func (c *Button) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg }
func (c *Button) SetActive(a bool) { c.active = a }
func (c *Button) IsActive() bool { return c.active }
func (c *Button) GetID() string { return c.id }
func (c *Button) SetID(newID string) { c.id = newID }
func (c *Button) GetX() int { return c.x }
func (c *Button) SetX(x int) { c.x = x }
func (c *Button) GetY() int { return c.y }
func (c *Button) SetY(y int) { c.y = y }
func (c *Button) GetWidth() int { return c.width }
func (c *Button) SetWidth(w int) { c.width = w }
func (c *Button) GetHeight() int { return c.height }
func (c *Button) SetHeight(h int) { c.height = h }
func (c *Button) GetFgColor() termbox.Attribute { return c.fg }
func (c *Button) SetFgColor(fg termbox.Attribute) { c.fg = fg }
func (c *Button) GetBgColor() termbox.Attribute { return c.bg }
func (c *Button) SetBgColor(bg termbox.Attribute) { c.bg = bg }
func (c *Button) IsBordered() bool { return c.bordered }
func (c *Button) SetBordered(bordered bool) { c.bordered = bordered }
func (c *Button) SetTabSkip(skip bool) { c.tabSkip = skip }
func (c *Button) IsTabSkipped() bool { return c.tabSkip }
func (c *Button) HandleEvent(e termbox.Event) bool {
return false
}
func (c *Button) Draw() {
stX, stY := c.x, c.y
if c.bordered {
DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg)
stX++
stY++
}
DrawStringAtPoint(c.label, stX, stY, c.fg, c.bg)
}

View File

@ -12,7 +12,8 @@ type ConfirmModal struct {
x, y, width, height int
showHelp bool
cursor int
bg, fg termbox.Attribute
fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
isDone bool
accepted bool
value string
@ -23,7 +24,7 @@ type ConfirmModal struct {
// CreateConfirmModal Creates a confirmation modal with the specified attributes
func CreateConfirmModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *ConfirmModal {
i := ConfirmModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg}
i := ConfirmModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, activeFg: fg, activeBg: bg}
if i.title == "" && i.text == "" {
i.title = "Confirm?"
}

View File

@ -4,174 +4,181 @@ import "github.com/nsf/termbox-go"
// DropMenu is a title that, when active drops a menu down
type DropMenu struct {
id string
title string
x, y, width, height int
bg, fg termbox.Attribute
selectedBg, selectedFg termbox.Attribute
menu *Menu
menuSelected bool
showMenu bool
bordered bool
tabSkip bool
id string
title string
x, y, width, height int
bg, fg termbox.Attribute
activeFg, activeBg termbox.Attribute
cursorBg, cursorFg termbox.Attribute
menu *Menu
menuSelected bool
showMenu bool
bordered bool
tabSkip bool
active bool
}
// CreateDropMenu Creates a menu with the specified attributes
func CreateDropMenu(title string, options []string, x, y, width, height int, fg, bg, selectedFg, selectedBg termbox.Attribute) *DropMenu {
i := DropMenu{
func CreateDropMenu(title string, options []string, x, y, width, height int, fg, bg, cursorFg, cursorBg termbox.Attribute) *DropMenu {
c := DropMenu{
title: title,
x: x, y: y, width: width, height: height,
fg: fg, bg: bg,
selectedFg: fg, selectedBg: bg,
fg: fg, bg: bg, activeFg: fg, activeBg: bg,
cursorFg: fg, cursorBg: bg,
}
i.menu = CreateMenu("", options, x, y+2, width, height, fg, bg)
return &i
c.menu = CreateMenu("", options, x, y+2, width, height, fg, bg)
return &c
}
// GetID returns this control's ID
func (i *DropMenu) GetID() string { return i.id }
func (c *DropMenu) GetID() string { return c.id }
// SetID sets this control's ID
func (i *DropMenu) SetID(newID string) {
i.id = newID
func (c *DropMenu) SetID(newID string) {
c.id = newID
}
func (c *DropMenu) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg }
func (c *DropMenu) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg }
func (c *DropMenu) SetActive(a bool) { c.active = a }
func (c *DropMenu) IsActive() bool { return c.active }
// GetTitle returns the current title of the menu
func (i *DropMenu) GetTitle() string { return i.title }
func (c *DropMenu) GetTitle() string { return c.title }
// SetTitle sets the current title of the menu to s
func (i *DropMenu) SetTitle(s string) {
i.title = s
func (c *DropMenu) SetTitle(s string) {
c.title = s
}
// GetMenu returns the menu for this dropmenu
func (i *DropMenu) GetMenu() *Menu {
return i.menu
func (c *DropMenu) GetMenu() *Menu {
return c.menu
}
// GetX returns the current x coordinate of the menu
func (i *DropMenu) GetX() int { return i.x }
func (c *DropMenu) GetX() int { return c.x }
// SetX sets the current x coordinate of the menu to x
func (i *DropMenu) SetX(x int) {
i.x = x
func (c *DropMenu) SetX(x int) {
c.x = x
}
// GetY returns the current y coordinate of the menu
func (i *DropMenu) GetY() int { return i.y }
func (c *DropMenu) GetY() int { return c.y }
// SetY sets the current y coordinate of the menu to y
func (i *DropMenu) SetY(y int) {
i.y = y
func (c *DropMenu) SetY(y int) {
c.y = y
}
// GetWidth returns the current width of the menu
func (i *DropMenu) GetWidth() int { return i.width }
func (c *DropMenu) GetWidth() int { return c.width }
// SetWidth sets the current menu width to width
func (i *DropMenu) SetWidth(width int) {
i.width = width
func (c *DropMenu) SetWidth(width int) {
c.width = width
}
// GetHeight returns the current height of the menu
func (i *DropMenu) GetHeight() int { return i.height }
func (c *DropMenu) GetHeight() int { return c.height }
// SetHeight set the height of the menu to height
func (i *DropMenu) SetHeight(height int) {
i.height = height
func (c *DropMenu) SetHeight(height int) {
c.height = height
}
// GetFgColor returns the foreground color
func (i *DropMenu) GetFgColor() termbox.Attribute { return i.fg }
func (c *DropMenu) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *DropMenu) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *DropMenu) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *DropMenu) GetBgColor() termbox.Attribute { return i.bg }
func (c *DropMenu) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *DropMenu) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *DropMenu) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
// IsBordered returns the bordered flag
func (i *DropMenu) IsBordered() bool { return i.bordered }
func (c *DropMenu) IsBordered() bool { return c.bordered }
// SetBordered sets the bordered flag
func (i *DropMenu) SetBordered(b bool) {
i.bordered = b
i.menu.SetBordered(b)
func (c *DropMenu) SetBordered(b bool) {
c.bordered = b
c.menu.SetBordered(b)
}
// IsDone returns whether the user has answered the modal
func (i *DropMenu) IsDone() bool { return i.menu.isDone }
func (c *DropMenu) IsDone() bool { return c.menu.isDone }
// SetDone sets whether the modal has completed it's purpose
func (i *DropMenu) SetDone(b bool) {
i.menu.isDone = b
func (c *DropMenu) SetDone(b bool) {
c.menu.isDone = b
}
// IsTabSkipped returns whether this modal has it's tabskip flag set
func (i *DropMenu) IsTabSkipped() bool {
return i.tabSkip
func (c *DropMenu) IsTabSkipped() bool {
return c.tabSkip
}
// SetTabSkip sets the tabskip flag for this control
func (i *DropMenu) SetTabSkip(b bool) {
i.tabSkip = b
func (c *DropMenu) SetTabSkip(b bool) {
c.tabSkip = b
}
// ShowMenu tells the menu to draw the options
func (i *DropMenu) ShowMenu() {
i.showMenu = true
i.menuSelected = true
func (c *DropMenu) ShowMenu() {
c.showMenu = true
c.menuSelected = true
}
// HideMenu tells the menu to hide the options
func (i *DropMenu) HideMenu() {
i.showMenu = false
i.menuSelected = false
func (c *DropMenu) HideMenu() {
c.showMenu = false
c.menuSelected = false
}
// HandleEvent handles the termbox event and returns whether it was consumed
func (i *DropMenu) HandleEvent(event termbox.Event) bool {
moveUp := (event.Key == termbox.KeyArrowUp || (i.menu.vimMode && event.Ch == 'k'))
moveDown := (event.Key == termbox.KeyArrowDown || (i.menu.vimMode && event.Ch == 'j'))
if i.menuSelected {
selIdx := i.menu.GetSelectedIndex()
if (moveUp && selIdx == 0) || (moveDown && selIdx == (len(i.menu.options)-1)) {
i.menuSelected = false
func (c *DropMenu) HandleEvent(event termbox.Event) bool {
moveUp := (event.Key == termbox.KeyArrowUp || (c.menu.vimMode && event.Ch == 'k'))
moveDown := (event.Key == termbox.KeyArrowDown || (c.menu.vimMode && event.Ch == 'j'))
if c.menuSelected {
selIdx := c.menu.GetSelectedIndex()
if (moveUp && selIdx == 0) || (moveDown && selIdx == (len(c.menu.options)-1)) {
c.menuSelected = false
} else {
if i.menu.HandleEvent(event) {
if i.menu.IsDone() {
i.HideMenu()
if c.menu.HandleEvent(event) {
if c.menu.IsDone() {
c.HideMenu()
}
return true
}
}
} else {
i.ShowMenu()
c.ShowMenu()
return true
}
return false
}
// Draw draws the menu
func (i *DropMenu) Draw() {
func (c *DropMenu) Draw() {
// The title
ttlFg, ttlBg := i.fg, i.bg
if !i.menuSelected {
ttlFg, ttlBg = i.selectedFg, i.selectedBg
ttlFg, ttlBg := c.fg, c.bg
if !c.menuSelected {
ttlFg, ttlBg = c.cursorFg, c.cursorBg
}
ttlTxt := i.title
if i.showMenu {
ttlTxt := c.title
if c.showMenu {
ttlTxt = ttlTxt + "-Showing Menu"
}
DrawStringAtPoint(AlignText(i.title, i.width, AlignLeft), i.x, i.y, ttlFg, ttlBg)
if i.showMenu {
i.menu.Draw()
DrawStringAtPoint(AlignText(c.title, c.width, AlignLeft), c.x, c.y, ttlFg, ttlBg)
if c.showMenu {
c.menu.Draw()
}
}

View File

@ -9,187 +9,252 @@ type Frame struct {
x, y, width, height int
tabIdx int
fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool
controls []termboxControl
tabSkip bool
active bool
title string
}
// CreateFrame creates a Frame at x, y that is w by h
func CreateFrame(x, y, w, h int, fg, bg termbox.Attribute) *Frame {
s := Frame{x: x, y: y, width: w, height: h, fg: fg, bg: bg, bordered: true}
return &s
c := Frame{x: x, y: y, width: w, height: h,
fg: fg, bg: bg, activeFg: fg, activeBg: bg,
bordered: true,
}
return &c
}
func (c *Frame) SetTitle(title string) { c.title = title }
// Setting color attributes on a frame trickles down to its controls
func (c *Frame) SetActiveFgColor(fg termbox.Attribute) {
c.activeFg = fg
for _, v := range c.controls {
v.SetActiveFgColor(fg)
}
}
func (c *Frame) SetActiveBgColor(bg termbox.Attribute) {
c.activeBg = bg
for _, v := range c.controls {
v.SetActiveBgColor(bg)
}
}
func (c *Frame) SetActive(a bool) {
c.active = a
for idx := range c.controls {
if idx == c.tabIdx && a {
c.controls[idx].SetActive(true)
} else {
c.controls[idx].SetActive(false)
}
}
}
func (c *Frame) IsActive() bool { return c.active }
// GetID returns this control's ID
func (i *Frame) GetID() string { return i.id }
func (c *Frame) GetID() string { return c.id }
// SetID sets this control's ID
func (i *Frame) SetID(newID string) {
i.id = newID
func (c *Frame) SetID(newID string) {
c.id = newID
}
// GetX returns the x position of the frame
func (i *Frame) GetX() int { return i.x }
func (c *Frame) GetX() int { return c.x }
// SetX sets the x position of the frame
func (i *Frame) SetX(x int) {
i.x = x
func (c *Frame) SetX(x int) {
c.x = x
}
// GetY returns the y position of the frame
func (i *Frame) GetY() int { return i.y }
func (c *Frame) GetY() int { return c.y }
// SetY sets the y position of the frame
func (i *Frame) SetY(y int) {
i.y = y
func (c *Frame) SetY(y int) {
c.y = y
}
// GetWidth returns the current width of the frame
func (i *Frame) GetWidth() int { return i.width }
func (c *Frame) GetWidth() int { return c.width }
// SetWidth sets the current width of the frame
func (i *Frame) SetWidth(w int) {
i.width = w
func (c *Frame) SetWidth(w int) {
c.width = w
}
// GetHeight returns the current height of the frame
func (i *Frame) GetHeight() int { return i.height }
func (c *Frame) GetHeight() int { return c.height }
// SetHeight sets the current height of the frame
func (i *Frame) SetHeight(h int) {
i.height = h
func (c *Frame) SetHeight(h int) {
c.height = h
}
// GetFgColor returns the foreground color
func (i *Frame) GetFgColor() termbox.Attribute { return i.fg }
func (c *Frame) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *Frame) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *Frame) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *Frame) GetBgColor() termbox.Attribute { return i.bg }
func (c *Frame) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *Frame) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *Frame) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
// IsBordered returns true or false if this frame has a border
func (i *Frame) IsBordered() bool { return i.bordered }
func (c *Frame) IsBordered() bool { return c.bordered }
// SetBordered sets whether we render a border around the frame
func (i *Frame) SetBordered(b bool) {
i.bordered = b
func (c *Frame) SetBordered(b bool) {
c.bordered = b
}
// IsTabSkipped returns whether this modal has it's tabskip flag set
func (i *Frame) IsTabSkipped() bool {
return i.tabSkip
func (c *Frame) IsTabSkipped() bool {
return c.tabSkip
}
// SetTabSkip sets the tabskip flag for this control
func (i *Frame) SetTabSkip(b bool) {
i.tabSkip = b
func (c *Frame) SetTabSkip(b bool) {
c.tabSkip = b
}
// AddControl adds a control to the frame
func (i *Frame) AddControl(t termboxControl) {
i.controls = append(i.controls, t)
func (c *Frame) AddControl(t termboxControl) {
c.controls = append(c.controls, t)
}
func (c *Frame) ResetTabIndex() {
for k, v := range c.controls {
if !v.IsTabSkipped() {
c.tabIdx = k
break
}
}
}
// GetActiveControl returns the control at tabIdx
func (i *Frame) GetActiveControl() termboxControl {
if len(i.controls) >= i.tabIdx {
return i.controls[i.tabIdx]
func (c *Frame) GetActiveControl() termboxControl {
if len(c.controls) >= c.tabIdx {
if c.controls[c.tabIdx].IsTabSkipped() {
c.FindNextTabStop()
}
return c.controls[c.tabIdx]
}
return nil
}
// GetControls returns a slice of all controls
func (i *Frame) GetControls() []termboxControl {
return i.controls
func (c *Frame) GetControls() []termboxControl {
return c.controls
}
// GetControl returns the control at index i
func (i *Frame) GetControl(idx int) termboxControl {
if len(i.controls) >= idx {
return i.controls[idx]
func (c *Frame) GetControl(idx int) termboxControl {
if len(c.controls) >= idx {
return c.controls[idx]
}
return nil
}
// GetControlCount returns the number of controls contained
func (i *Frame) GetControlCount() int {
return len(i.controls)
func (c *Frame) GetControlCount() int {
return len(c.controls)
}
// GetLastControl returns the last control contained
func (i *Frame) GetLastControl() termboxControl {
return i.controls[len(i.controls)-1]
func (c *Frame) GetLastControl() termboxControl {
return c.controls[len(c.controls)-1]
}
// RemoveAllControls clears the control slice
func (i *Frame) RemoveAllControls() {
i.controls = []termboxControl{}
func (c *Frame) RemoveAllControls() {
c.controls = []termboxControl{}
}
// DrawControl figures out the relative position of the control,
// sets it, draws it, then resets it.
func (i *Frame) DrawControl(t termboxControl) {
func (c *Frame) DrawControl(t termboxControl) {
ctlX, ctlY := t.GetX(), t.GetY()
t.SetX((i.GetX() + ctlX))
t.SetY((i.GetY() + ctlY))
t.SetX((c.GetX() + ctlX))
t.SetY((c.GetY() + ctlY))
t.Draw()
t.SetX(ctlX)
t.SetY(ctlY)
}
// GetBottomY returns the y of the lowest control in the frame
func (i *Frame) GetBottomY() int {
func (c *Frame) GetBottomY() int {
var ret int
for idx := range i.controls {
if i.controls[idx].GetY()+i.controls[idx].GetHeight() > ret {
ret = i.controls[idx].GetY() + i.controls[idx].GetHeight()
for idx := range c.controls {
if c.controls[idx].GetY()+c.controls[idx].GetHeight() > ret {
ret = c.controls[idx].GetY() + c.controls[idx].GetHeight()
}
}
return ret
}
// HandleEvent accepts the termbox event and returns whether it was consumed
func (i *Frame) HandleEvent(event termbox.Event) bool {
func (c *Frame) HandleEvent(event termbox.Event) bool {
if event.Key == termbox.KeyTab {
i.FindNextTabStop()
c.FindNextTabStop()
return true
}
return i.controls[i.tabIdx].HandleEvent(event)
return c.controls[c.tabIdx].HandleEvent(event)
}
// FindNextTabStop finds the next control that can be tabbed to
// A return of true means it found a different one than we started on.
func (i *Frame) FindNextTabStop() bool {
startTab := i.tabIdx
i.tabIdx = (i.tabIdx + 1) % len(i.controls)
for i.controls[i.tabIdx].IsTabSkipped() {
i.tabIdx = (i.tabIdx + 1) % len(i.controls)
if i.tabIdx == startTab {
func (c *Frame) FindNextTabStop() bool {
startTab := c.tabIdx
c.tabIdx = (c.tabIdx + 1) % len(c.controls)
for c.controls[c.tabIdx].IsTabSkipped() {
c.tabIdx = (c.tabIdx + 1) % len(c.controls)
if c.tabIdx == startTab {
break
}
}
return i.tabIdx != startTab
return c.tabIdx != startTab
}
// IsOnLastControl returns true if the active control
// is the last control that isn't tab skippable.
func (c *Frame) IsOnLastControl() bool {
for _, v := range c.controls[c.tabIdx+1:] {
if !v.IsTabSkipped() {
return false
}
}
return true
}
// Draw outputs the Scoll Frame on the screen
func (i *Frame) Draw() {
maxWidth := i.width
maxHeight := i.height
x, y := i.x, i.y
startX := i.x
startY := i.y
if i.bordered {
FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
func (c *Frame) Draw() {
maxWidth := c.width
maxHeight := c.height
x, y := c.x, c.y
startX := c.x
startY := c.y
borderFg, borderBg := c.fg, c.bg
if c.active {
borderFg, borderBg = c.activeFg, c.activeBg
}
if c.bordered {
// Clear the framed area
FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, borderFg, borderBg)
if c.title == "" {
DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, borderFg, borderBg)
} else {
DrawBorderWithTitle(c.x, c.y, c.x+c.width, c.y+c.height, " "+c.title+" ", borderFg, borderBg)
}
maxWidth--
maxHeight--
x++
@ -197,7 +262,12 @@ func (i *Frame) Draw() {
startX++
startY++
}
for idx := range i.controls {
i.DrawControl(i.controls[idx])
for idx := range c.controls {
if idx == c.tabIdx {
c.controls[idx].SetActive(true)
} else {
c.controls[idx].SetActive(false)
}
c.DrawControl(c.controls[idx])
}
}

View File

@ -5,153 +5,164 @@ import "github.com/nsf/termbox-go"
// InputField is a field for inputting text
type InputField struct {
id string
title string
value string
x, y, width, height int
cursor int
fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
cursorFg, cursorBg termbox.Attribute
bordered bool
wrap bool
multiline bool
tabSkip bool
active bool
}
// CreateInputField creates an input field at x, y that is w by h
func CreateInputField(x, y, w, h int, fg, bg termbox.Attribute) *InputField {
i := InputField{x: x, y: y, width: w, height: h, fg: fg, bg: bg, cursorFg: bg, cursorBg: fg}
return &i
c := InputField{x: x, y: y, width: w, height: h,
fg: fg, bg: bg, cursorFg: bg, cursorBg: fg, activeFg: fg, activeBg: bg,
}
return &c
}
func (c *InputField) SetTitle(title string) { c.title = title }
func (c *InputField) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg }
func (c *InputField) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg }
func (c *InputField) SetActive(a bool) { c.active = a }
func (c *InputField) IsActive() bool { return c.active }
// GetID returns this control's ID
func (i *InputField) GetID() string { return i.id }
func (c *InputField) GetID() string { return c.id }
// SetID sets this control's ID
func (i *InputField) SetID(newID string) {
i.id = newID
func (c *InputField) SetID(newID string) {
c.id = newID
}
// GetValue gets the current text that is in the InputField
func (i *InputField) GetValue() string { return i.value }
func (c *InputField) GetValue() string { return c.value }
// SetValue sets the current text in the InputField to s
func (i *InputField) SetValue(s string) {
i.value = s
func (c *InputField) SetValue(s string) {
c.value = s
}
// GetX returns the x position of the input field
func (i *InputField) GetX() int { return i.x }
func (c *InputField) GetX() int { return c.x }
// SetX sets the x position of the input field
func (i *InputField) SetX(x int) {
i.x = x
func (c *InputField) SetX(x int) {
c.x = x
}
// GetY returns the y position of the input field
func (i *InputField) GetY() int { return i.y }
func (c *InputField) GetY() int { return c.y }
// SetY sets the y position of the input field
func (i *InputField) SetY(y int) {
i.y = y
func (c *InputField) SetY(y int) {
c.y = y
}
// GetWidth returns the current width of the input field
func (i *InputField) GetWidth() int { return i.width }
func (c *InputField) GetWidth() int { return c.width }
// SetWidth sets the current width of the input field
func (i *InputField) SetWidth(w int) {
i.width = w
func (c *InputField) SetWidth(w int) {
c.width = w
}
// GetHeight returns the current height of the input field
func (i *InputField) GetHeight() int { return i.height }
func (c *InputField) GetHeight() int { return c.height }
// SetHeight sets the current height of the input field
func (i *InputField) SetHeight(h int) {
i.height = h
func (c *InputField) SetHeight(h int) {
c.height = h
}
// GetFgColor returns the foreground color
func (i *InputField) GetFgColor() termbox.Attribute { return i.fg }
func (c *InputField) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *InputField) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *InputField) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *InputField) GetBgColor() termbox.Attribute { return i.bg }
func (c *InputField) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *InputField) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *InputField) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
func (i *InputField) SetCursorFg(fg termbox.Attribute) {
i.cursorFg = fg
func (c *InputField) SetCursorFg(fg termbox.Attribute) {
c.cursorFg = fg
}
func (i *InputField) GetCursorFg() termbox.Attribute { return i.cursorFg }
func (c *InputField) GetCursorFg() termbox.Attribute { return c.cursorFg }
func (i *InputField) SetCursorBg(bg termbox.Attribute) {
i.cursorBg = bg
func (c *InputField) SetCursorBg(bg termbox.Attribute) {
c.cursorBg = bg
}
func (i *InputField) GetCursorBg() termbox.Attribute { return i.cursorBg }
func (c *InputField) GetCursorBg() termbox.Attribute { return c.cursorBg }
// IsBordered returns true or false if this input field has a border
func (i *InputField) IsBordered() bool { return i.bordered }
func (c *InputField) IsBordered() bool { return c.bordered }
// SetBordered sets whether we render a border around the input field
func (i *InputField) SetBordered(b bool) {
i.bordered = b
func (c *InputField) SetBordered(b bool) {
c.bordered = b
}
// IsTabSkipped returns whether this modal has it's tabskip flag set
func (i *InputField) IsTabSkipped() bool {
return i.tabSkip
func (c *InputField) IsTabSkipped() bool {
return c.tabSkip
}
// SetTabSkip sets the tabskip flag for this control
func (i *InputField) SetTabSkip(b bool) {
i.tabSkip = b
func (c *InputField) SetTabSkip(b bool) {
c.tabSkip = b
}
// DoesWrap returns true or false if this input field wraps text
func (i *InputField) DoesWrap() bool { return i.wrap }
func (c *InputField) DoesWrap() bool { return c.wrap }
// SetWrap sets whether we wrap the text at width.
func (i *InputField) SetWrap(b bool) {
i.wrap = b
func (c *InputField) SetWrap(b bool) {
c.wrap = b
}
// IsMultiline returns true or false if this field can have multiple lines
func (i *InputField) IsMultiline() bool { return i.multiline }
func (c *InputField) IsMultiline() bool { return c.multiline }
// SetMultiline sets whether the field can have multiple lines
func (i *InputField) SetMultiline(b bool) {
i.multiline = b
func (c *InputField) SetMultiline(b bool) {
c.multiline = b
}
// HandleEvent accepts the termbox event and returns whether it was consumed
func (i *InputField) HandleEvent(event termbox.Event) bool {
func (c *InputField) HandleEvent(event termbox.Event) bool {
if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
if i.cursor+len(i.value) > 0 {
crs := len(i.value)
if i.cursor < 0 {
crs = i.cursor + len(i.value)
if c.cursor+len(c.value) > 0 {
crs := len(c.value)
if c.cursor < 0 {
crs = c.cursor + len(c.value)
}
i.value = i.value[:crs-1] + i.value[crs:]
//i.value = i.value[:len(i.value)-1]
c.value = c.value[:crs-1] + c.value[crs:]
//c.value = c.value[:len(c.value)-1]
}
} else if event.Key == termbox.KeyArrowLeft {
if i.cursor+len(i.value) > 0 {
i.cursor--
if c.cursor+len(c.value) > 0 {
c.cursor--
}
} else if event.Key == termbox.KeyArrowRight {
if i.cursor < 0 {
i.cursor++
if c.cursor < 0 {
c.cursor++
}
} else if event.Key == termbox.KeyCtrlU {
// Ctrl+U Clears the Input (before the cursor)
i.value = i.value[i.cursor+len(i.value):]
c.value = c.value[c.cursor+len(c.value):]
} else {
// Get the rune to add to our value. Space and Tab are special cases where
// we can't use the event's rune directly
@ -162,7 +173,7 @@ func (i *InputField) HandleEvent(event termbox.Event) bool {
case termbox.KeyTab:
ch = "\t"
case termbox.KeyEnter:
if i.multiline {
if c.multiline {
ch = "\n"
}
default:
@ -172,28 +183,32 @@ func (i *InputField) HandleEvent(event termbox.Event) bool {
}
// TODO: Handle newlines
if i.cursor+len(i.value) == 0 {
i.value = string(ch) + i.value
} else if i.cursor == 0 {
i.value = i.value + string(ch)
if c.cursor+len(c.value) == 0 {
c.value = string(ch) + c.value
} else if c.cursor == 0 {
c.value = c.value + string(ch)
} else {
strPt1 := i.value[:(len(i.value) + i.cursor)]
strPt2 := i.value[(len(i.value) + i.cursor):]
i.value = strPt1 + string(ch) + strPt2
strPt1 := c.value[:(len(c.value) + c.cursor)]
strPt2 := c.value[(len(c.value) + c.cursor):]
c.value = strPt1 + string(ch) + strPt2
}
}
return true
}
// Draw outputs the input field on the screen
func (i *InputField) Draw() {
maxWidth := i.width
maxHeight := i.height
x, y := i.x, i.y
startX := i.x
startY := i.y
if i.bordered {
DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
func (c *InputField) Draw() {
maxWidth := c.width
maxHeight := c.height
x, y := c.x, c.y
startX := c.x
startY := c.y
useFg, useBg := c.fg, c.bg
if c.active {
useFg, useBg = c.activeFg, c.activeBg
}
if c.bordered {
DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, useFg, useBg)
maxWidth--
maxHeight--
x++
@ -204,53 +219,60 @@ func (i *InputField) Draw() {
var strPt1, strPt2 string
var cursorRune rune
if len(i.value) > 0 {
if i.cursor+len(i.value) == 0 {
if len(c.value) > 0 {
if c.cursor+len(c.value) == 0 {
strPt1 = ""
strPt2 = i.value[1:]
cursorRune = rune(i.value[0])
} else if i.cursor == 0 {
strPt1 = i.value
strPt2 = c.value[1:]
cursorRune = rune(c.value[0])
} else if c.cursor == 0 {
strPt1 = c.value
strPt2 = ""
cursorRune = ' '
} else {
strPt1 = i.value[:(len(i.value) + i.cursor)]
strPt2 = i.value[(len(i.value)+i.cursor)+1:]
cursorRune = rune(i.value[len(i.value)+i.cursor])
strPt1 = c.value[:(len(c.value) + c.cursor)]
strPt2 = c.value[(len(c.value)+c.cursor)+1:]
cursorRune = rune(c.value[len(c.value)+c.cursor])
}
} else {
strPt1, strPt2, cursorRune = "", "", ' '
}
if i.wrap {
if c.title != "" {
if c.active {
DrawStringAtPoint(c.title, x, y, c.activeFg, c.activeBg)
} else {
DrawStringAtPoint(c.title, x, y, useFg, useBg)
}
}
if c.wrap {
// Split the text into maxWidth chunks
for len(strPt1) > maxWidth {
breakAt := maxWidth
DrawStringAtPoint(strPt1[:breakAt], x, y, i.fg, i.bg)
DrawStringAtPoint(strPt1[:breakAt], x, y, useFg, useBg)
x = startX
y++
strPt1 = strPt1[breakAt:]
}
x, y = DrawStringAtPoint(strPt1, x, y, i.fg, i.bg)
x, y = DrawStringAtPoint(strPt1, x, y, useFg, useBg)
if x >= maxWidth {
y++
x = startX
}
termbox.SetCell(x, y, cursorRune, i.cursorFg, i.cursorBg)
termbox.SetCell(x, y, cursorRune, c.cursorFg, c.cursorBg)
x++
if len(strPt2) > 0 {
lenLeft := maxWidth - len(strPt1) - 1
if lenLeft > 0 && len(strPt2) > lenLeft {
DrawStringAtPoint(strPt2[:lenLeft], x+1, y, i.fg, i.bg)
DrawStringAtPoint(strPt2[:lenLeft], x+1, y, useFg, useBg)
strPt2 = strPt2[lenLeft:]
}
for len(strPt2) > maxWidth {
breakAt := maxWidth
DrawStringAtPoint(strPt2[:breakAt], x, y, i.fg, i.bg)
DrawStringAtPoint(strPt2[:breakAt], x, y, useFg, useBg)
x = startX
y++
strPt2 = strPt2[breakAt:]
}
x, y = DrawStringAtPoint(strPt2, x, y, i.fg, i.bg)
x, y = DrawStringAtPoint(strPt2, x, y, useFg, useBg)
}
} else {
for len(strPt1)+len(strPt2)+1 > maxWidth {
@ -263,8 +285,12 @@ func (i *InputField) Draw() {
strPt2 = strPt2[:len(strPt2)-1]
}
}
x, y = DrawStringAtPoint(strPt1, i.x+1, i.y+1, i.fg, i.bg)
termbox.SetCell(x, y, cursorRune, i.cursorFg, i.cursorBg)
DrawStringAtPoint(strPt2, x+1, y, i.fg, i.bg)
x, y = DrawStringAtPoint(strPt1, c.x+len(c.title), c.y, useFg, useBg)
if c.active {
termbox.SetCell(x, y, cursorRune, c.cursorFg, c.cursorBg)
} else {
termbox.SetCell(x, y, cursorRune, useFg, useBg)
}
DrawStringAtPoint(strPt2, x+1, y, useFg, useBg)
}
}

View File

@ -14,244 +14,251 @@ type InputModal struct {
showHelp bool
cursor int
bg, fg termbox.Attribute
activeFg, activeBg termbox.Attribute
isDone bool
isAccepted bool
isVisible bool
bordered bool
tabSkip bool
inputSelected bool
active bool
}
// CreateInputModal Create an input modal with the given attributes
func CreateInputModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *InputModal {
i := InputModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, bordered: true}
i.input = CreateInputField(i.x+2, i.y+3, i.width-2, 2, i.fg, i.bg)
i.showHelp = true
i.input.bordered = true
i.isVisible = true
i.inputSelected = true
return &i
c := InputModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, bordered: true}
c.input = CreateInputField(c.x+2, c.y+3, c.width-2, 2, c.fg, c.bg)
c.showHelp = true
c.input.bordered = true
c.isVisible = true
c.inputSelected = true
return &c
}
func (c *InputModal) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg }
func (c *InputModal) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg }
func (c *InputModal) SetActive(a bool) { c.active = a }
func (c *InputModal) IsActive() bool { return c.active }
// GetID returns this control's ID
func (i *InputModal) GetID() string { return i.id }
func (c *InputModal) GetID() string { return c.id }
// SetID sets this control's ID
func (i *InputModal) SetID(newID string) {
i.id = newID
func (c *InputModal) SetID(newID string) {
c.id = newID
}
// GetTitle Return the title of the modal
func (i *InputModal) GetTitle() string { return i.title }
func (c *InputModal) GetTitle() string { return c.title }
// SetTitle Sets the title of the modal to s
func (i *InputModal) SetTitle(s string) {
i.title = s
func (c *InputModal) SetTitle(s string) {
c.title = s
}
// GetText Return the text of the modal
func (i *InputModal) GetText() string { return i.text }
func (c *InputModal) GetText() string { return c.text }
// SetText Set the text of the modal to s
func (i *InputModal) SetText(s string) {
i.text = s
func (c *InputModal) SetText(s string) {
c.text = s
}
// GetX Return the x position of the modal
func (i *InputModal) GetX() int { return i.x }
func (c *InputModal) GetX() int { return c.x }
// SetX set the x position of the modal to x
func (i *InputModal) SetX(x int) {
i.x = x
func (c *InputModal) SetX(x int) {
c.x = x
}
// GetY Return the y position of the modal
func (i *InputModal) GetY() int { return i.y }
func (c *InputModal) GetY() int { return c.y }
// SetY Set the y position of the modal to y
func (i *InputModal) SetY(y int) {
i.y = y
func (c *InputModal) SetY(y int) {
c.y = y
}
// GetWidth Return the width of the modal
func (i *InputModal) GetWidth() int { return i.width }
func (c *InputModal) GetWidth() int { return c.width }
// SetWidth Set the width of the modal to width
func (i *InputModal) SetWidth(width int) {
i.width = width
func (c *InputModal) SetWidth(width int) {
c.width = width
}
// GetHeight Return the height of the modal
func (i *InputModal) GetHeight() int { return i.height }
func (c *InputModal) GetHeight() int { return c.height }
// SetHeight Set the height of the modal to height
func (i *InputModal) SetHeight(height int) {
i.height = height
func (c *InputModal) SetHeight(height int) {
c.height = height
}
// SetMultiline returns whether this is a multiline modal
func (i *InputModal) SetMultiline(m bool) {
i.input.multiline = m
func (c *InputModal) SetMultiline(m bool) {
c.input.multiline = m
}
// IsMultiline returns whether this is a multiline modal
func (i *InputModal) IsMultiline() bool {
return i.input.multiline
func (c *InputModal) IsMultiline() bool {
return c.input.multiline
}
// IsBordered returns whether this control is bordered or not
func (i *InputModal) IsBordered() bool {
return i.bordered
func (c *InputModal) IsBordered() bool {
return c.bordered
}
// SetBordered sets whether we render a border around the frame
func (i *InputModal) SetBordered(b bool) {
i.bordered = b
func (c *InputModal) SetBordered(b bool) {
c.bordered = b
}
// IsTabSkipped returns whether this control has it's tabskip flag set
func (i *InputModal) IsTabSkipped() bool {
return i.tabSkip
func (c *InputModal) IsTabSkipped() bool {
return c.tabSkip
}
// SetTabSkip sets the tabskip flag for this control
func (i *InputModal) SetTabSkip(b bool) {
i.tabSkip = b
func (c *InputModal) SetTabSkip(b bool) {
c.tabSkip = b
}
// HelpIsShown Returns whether the modal is showing it's help text or not
func (i *InputModal) HelpIsShown() bool { return i.showHelp }
func (c *InputModal) HelpIsShown() bool { return c.showHelp }
// ShowHelp Set the "Show Help" flag
func (i *InputModal) ShowHelp(b bool) {
i.showHelp = b
func (c *InputModal) ShowHelp(b bool) {
c.showHelp = b
}
// GetFgColor returns the foreground color
func (i *InputModal) GetFgColor() termbox.Attribute { return i.fg }
func (c *InputModal) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *InputModal) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *InputModal) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *InputModal) GetBgColor() termbox.Attribute { return i.bg }
func (c *InputModal) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *InputModal) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *InputModal) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
// Show Sets the visibility flag to true
func (i *InputModal) Show() {
i.isVisible = true
func (c *InputModal) Show() {
c.isVisible = true
}
// Hide Sets the visibility flag to false
func (i *InputModal) Hide() {
i.isVisible = false
func (c *InputModal) Hide() {
c.isVisible = false
}
// IsVisible returns the isVisible flag
func (i *InputModal) IsVisible() bool {
return i.isVisible
func (c *InputModal) IsVisible() bool {
return c.isVisible
}
// SetDone Sets the flag that tells whether this modal has completed it's purpose
func (i *InputModal) SetDone(b bool) {
i.isDone = b
func (c *InputModal) SetDone(b bool) {
c.isDone = b
}
// IsDone Returns the "isDone" flag
func (i *InputModal) IsDone() bool {
return i.isDone
func (c *InputModal) IsDone() bool {
return c.isDone
}
// IsAccepted Returns whether the modal has been accepted
func (i *InputModal) IsAccepted() bool {
return i.isAccepted
func (c *InputModal) IsAccepted() bool {
return c.isAccepted
}
// GetValue Return the current value of the input
func (i *InputModal) GetValue() string { return i.input.GetValue() }
func (c *InputModal) GetValue() string { return c.input.GetValue() }
// SetValue Sets the value of the input to s
func (i *InputModal) SetValue(s string) {
i.input.SetValue(s)
func (c *InputModal) SetValue(s string) {
c.input.SetValue(s)
}
// SetInputWrap sets whether the input field will wrap long text or not
func (i *InputModal) SetInputWrap(b bool) {
i.input.SetWrap(b)
func (c *InputModal) SetInputWrap(b bool) {
c.input.SetWrap(b)
}
// Clear Resets all non-positional parameters of the modal
func (i *InputModal) Clear() {
i.title = ""
i.text = ""
i.input.SetValue("")
i.isDone = false
i.isVisible = false
func (c *InputModal) Clear() {
c.title = ""
c.text = ""
c.input.SetValue("")
c.isDone = false
c.isVisible = false
}
// HandleEvent Handle the termbox event, return true if it was consumed
func (i *InputModal) HandleEvent(event termbox.Event) bool {
func (c *InputModal) HandleEvent(event termbox.Event) bool {
if event.Key == termbox.KeyEnter {
if !i.input.IsMultiline() || !i.inputSelected {
if !c.input.IsMultiline() || !c.inputSelected {
// Done editing
i.isDone = true
i.isAccepted = true
c.isDone = true
c.isAccepted = true
} else {
i.input.HandleEvent(event)
c.input.HandleEvent(event)
}
return true
} else if event.Key == termbox.KeyTab {
if i.input.IsMultiline() {
i.inputSelected = !i.inputSelected
if c.input.IsMultiline() {
c.inputSelected = !c.inputSelected
}
} else if event.Key == termbox.KeyEsc {
// Done editing
i.isDone = true
i.isAccepted = false
c.isDone = true
c.isAccepted = false
return true
}
return i.input.HandleEvent(event)
return c.input.HandleEvent(event)
}
// Draw Draw the modal
func (i *InputModal) Draw() {
if i.isVisible {
func (c *InputModal) Draw() {
if c.isVisible {
// First blank out the area we'll be putting the modal
FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
nextY := i.y + 1
FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg)
nextY := c.y + 1
// The title
if i.title != "" {
if len(i.title) > i.width {
diff := i.width - len(i.title)
DrawStringAtPoint(i.title[:len(i.title)+diff-1], i.x+1, nextY, i.fg, i.bg)
if c.title != "" {
if len(c.title) > c.width {
diff := c.width - len(c.title)
DrawStringAtPoint(c.title[:len(c.title)+diff-1], c.x+1, nextY, c.fg, c.bg)
} else {
DrawStringAtPoint(i.title, i.x+1, nextY, i.fg, i.bg)
DrawStringAtPoint(c.title, c.x+1, nextY, c.fg, c.bg)
}
nextY++
FillWithChar('-', i.x+1, nextY, i.x+i.width-1, nextY, i.fg, i.bg)
FillWithChar('-', c.x+1, nextY, c.x+c.width-1, nextY, c.fg, c.bg)
nextY++
}
if i.text != "" {
DrawStringAtPoint(i.text, i.x+1, nextY, i.fg, i.bg)
if c.text != "" {
DrawStringAtPoint(c.text, c.x+1, nextY, c.fg, c.bg)
nextY++
}
i.input.SetY(nextY)
i.input.Draw()
c.input.SetY(nextY)
c.input.Draw()
nextY += 3
if i.showHelp {
if c.showHelp {
helpString := " (ENTER) to Accept. (ESC) to Cancel. "
helpX := (i.x + i.width - len(helpString)) - 1
DrawStringAtPoint(helpString, helpX, nextY, i.fg, i.bg)
helpX := (c.x + c.width - len(helpString)) - 1
DrawStringAtPoint(helpString, helpX, nextY, c.fg, c.bg)
}
if i.bordered {
if c.bordered {
// Now draw the border
DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg)
}
}
}

View File

@ -9,133 +9,133 @@ type Label struct {
x, y, width, height int
cursor int
fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool
wrap bool
multiline bool
active bool
}
// CreateLabel creates an input field at x, y that is w by h
func CreateLabel(lbl string, x, y, w, h int, fg, bg termbox.Attribute) *Label {
i := Label{value: lbl, x: x, y: y, width: w, height: h, fg: fg, bg: bg}
return &i
c := Label{
value: lbl, x: x, y: y, width: w, height: h,
fg: fg, bg: bg, activeFg: fg, activeBg: bg,
}
return &c
}
func (c *Label) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg }
func (c *Label) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg }
func (c *Label) SetActive(a bool) { c.active = a }
func (c *Label) IsActive() bool { return c.active }
// IsTabSkipped is always true for a label
func (i *Label) IsTabSkipped() bool {
return true
}
func (c *Label) IsTabSkipped() bool { return true }
// This doesn't do anything for a label
func (i *Label) SetTabSkip(b bool) {}
func (c *Label) SetTabSkip(b bool) {}
// GetID returns this control's ID
func (i *Label) GetID() string { return i.id }
func (c *Label) GetID() string { return c.id }
// SetID sets this control's ID
func (i *Label) SetID(newID string) {
i.id = newID
}
func (c *Label) SetID(newID string) { c.id = newID }
// GetValue gets the current text that is in the Label
func (i *Label) GetValue() string { return i.value }
func (c *Label) GetValue() string { return c.value }
// SetValue sets the current text in the Label to s
func (i *Label) SetValue(s string) {
i.value = s
}
func (c *Label) SetValue(s string) { c.value = s }
// GetX returns the x position of the input field
func (i *Label) GetX() int { return i.x }
func (c *Label) GetX() int { return c.x }
// SetX sets the x position of the input field
func (i *Label) SetX(x int) {
i.x = x
}
func (c *Label) SetX(x int) { c.x = x }
// GetY returns the y position of the input field
func (i *Label) GetY() int { return i.y }
func (c *Label) GetY() int { return c.y }
// SetY sets the y position of the input field
func (i *Label) SetY(y int) {
i.y = y
}
func (c *Label) SetY(y int) { c.y = y }
// GetWidth returns the current width of the input field
func (i *Label) GetWidth() int {
if i.width == -1 {
if i.bordered {
return len(i.value) + 2
func (c *Label) GetWidth() int {
if c.width == -1 {
if c.bordered {
return len(c.value) + 2
}
return len(i.value)
return len(c.value)
}
return i.width
return c.width
}
// SetWidth sets the current width of the input field
func (i *Label) SetWidth(w int) {
i.width = w
func (c *Label) SetWidth(w int) {
c.width = w
}
// GetHeight returns the current height of the input field
func (i *Label) GetHeight() int { return i.height }
func (c *Label) GetHeight() int { return c.height }
// SetHeight sets the current height of the input field
func (i *Label) SetHeight(h int) {
i.height = h
func (c *Label) SetHeight(h int) {
c.height = h
}
// GetFgColor returns the foreground color
func (i *Label) GetFgColor() termbox.Attribute { return i.fg }
func (c *Label) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *Label) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *Label) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *Label) GetBgColor() termbox.Attribute { return i.bg }
func (c *Label) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *Label) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *Label) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
// IsBordered returns true or false if this input field has a border
func (i *Label) IsBordered() bool { return i.bordered }
func (c *Label) IsBordered() bool { return c.bordered }
// SetBordered sets whether we render a border around the input field
func (i *Label) SetBordered(b bool) {
i.bordered = b
func (c *Label) SetBordered(b bool) {
c.bordered = b
}
// DoesWrap returns true or false if this input field wraps text
func (i *Label) DoesWrap() bool { return i.wrap }
func (c *Label) DoesWrap() bool { return c.wrap }
// SetWrap sets whether we wrap the text at width.
func (i *Label) SetWrap(b bool) {
i.wrap = b
func (c *Label) SetWrap(b bool) {
c.wrap = b
}
// IsMultiline returns true or false if this field can have multiple lines
func (i *Label) IsMultiline() bool { return i.multiline }
func (c *Label) IsMultiline() bool { return c.multiline }
// SetMultiline sets whether the field can have multiple lines
func (i *Label) SetMultiline(b bool) {
i.multiline = b
func (c *Label) SetMultiline(b bool) {
c.multiline = b
}
// HandleEvent accepts the termbox event and returns whether it was consumed
func (i *Label) HandleEvent(event termbox.Event) bool { return false }
func (c *Label) HandleEvent(event termbox.Event) bool { return false }
// Draw outputs the input field on the screen
func (i *Label) Draw() {
maxWidth := i.width
maxHeight := i.height
x, y := i.x, i.y
startX := i.x
startY := i.y
if i.bordered {
DrawBorder(i.x, i.y, i.x+i.GetWidth(), i.y+i.height, i.fg, i.bg)
func (c *Label) Draw() {
maxWidth := c.width
maxHeight := c.height
x, y := c.x, c.y
startX := c.x
startY := c.y
if c.bordered {
DrawBorder(c.x, c.y, c.x+c.GetWidth(), c.y+c.height, c.fg, c.bg)
maxWidth--
maxHeight--
x++
@ -144,5 +144,5 @@ func (i *Label) Draw() {
startY++
}
DrawStringAtPoint(i.value, x, y, i.fg, i.bg)
DrawStringAtPoint(c.value, x, y, c.fg, c.bg)
}

View File

@ -14,118 +14,134 @@ type Menu struct {
bg, fg termbox.Attribute
selectedBg, selectedFg termbox.Attribute
disabledBg, disabledFg termbox.Attribute
activeFg, activeBg termbox.Attribute
isDone bool
bordered bool
vimMode bool
tabSkip bool
active 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{
c := Menu{
title: title,
x: x, y: y, width: width, height: height,
fg: fg, bg: bg, selectedFg: bg, selectedBg: fg,
disabledFg: bg, disabledBg: bg,
activeFg: fg, activeBg: bg,
bordered: true,
}
for _, line := range options {
i.options = append(i.options, MenuOption{text: line})
c.options = append(c.options, MenuOption{text: line})
}
if len(i.options) > 0 {
i.SetSelectedOption(&i.options[0])
if len(c.options) > 0 {
c.SetSelectedOption(&c.options[0])
}
return &i
return &c
}
func (c *Menu) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg }
func (c *Menu) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg }
func (c *Menu) SetActive(a bool) { c.active = a }
func (c *Menu) IsActive() bool { return c.active }
// GetID returns this control's ID
func (i *Menu) GetID() string { return i.id }
func (c *Menu) GetID() string { return c.id }
// SetID sets this control's ID
func (i *Menu) SetID(newID string) {
i.id = newID
}
func (c *Menu) SetID(newID string) { c.id = newID }
// IsTabSkipped is always true for a label
func (c *Menu) IsTabSkipped() bool { return true }
// This doesn't do anything for a label
func (c *Menu) SetTabSkip(b bool) {}
// GetTitle returns the current title of the menu
func (i *Menu) GetTitle() string { return i.title }
func (c *Menu) GetTitle() string { return c.title }
// SetTitle sets the current title of the menu to s
func (i *Menu) SetTitle(s string) {
i.title = s
func (c *Menu) SetTitle(s string) {
c.title = s
}
// GetOptions returns the current options of the menu
func (i *Menu) GetOptions() []MenuOption {
return i.options
func (c *Menu) GetOptions() []MenuOption {
return c.options
}
// SetOptions set the menu's options to opts
func (i *Menu) SetOptions(opts []MenuOption) {
i.options = opts
func (c *Menu) SetOptions(opts []MenuOption) {
c.options = opts
}
// SetOptionsFromStrings sets the options of this menu from a slice of strings
func (i *Menu) SetOptionsFromStrings(opts []string) {
func (c *Menu) SetOptionsFromStrings(opts []string) {
var newOpts []MenuOption
for _, v := range opts {
newOpts = append(newOpts, *CreateOptionFromText(v))
}
i.SetOptions(newOpts)
i.SetSelectedOption(i.GetOptionFromIndex(0))
c.SetOptions(newOpts)
c.SetSelectedOption(c.GetOptionFromIndex(0))
}
// GetX returns the current x coordinate of the menu
func (i *Menu) GetX() int { return i.x }
func (c *Menu) GetX() int { return c.x }
// SetX sets the current x coordinate of the menu to x
func (i *Menu) SetX(x int) {
i.x = x
func (c *Menu) SetX(x int) {
c.x = x
}
// GetY returns the current y coordinate of the menu
func (i *Menu) GetY() int { return i.y }
func (c *Menu) GetY() int { return c.y }
// SetY sets the current y coordinate of the menu to y
func (i *Menu) SetY(y int) {
i.y = y
func (c *Menu) SetY(y int) {
c.y = y
}
// GetWidth returns the current width of the menu
func (i *Menu) GetWidth() int { return i.width }
func (c *Menu) GetWidth() int { return c.width }
// SetWidth sets the current menu width to width
func (i *Menu) SetWidth(width int) {
i.width = width
func (c *Menu) SetWidth(width int) {
c.width = width
}
// GetHeight returns the current height of the menu
func (i *Menu) GetHeight() int { return i.height }
func (c *Menu) GetHeight() int { return c.height }
// SetHeight set the height of the menu to height
func (i *Menu) SetHeight(height int) {
i.height = height
func (c *Menu) SetHeight(height int) {
c.height = height
}
// GetSelectedOption returns the current selected option
func (i *Menu) GetSelectedOption() *MenuOption {
idx := i.GetSelectedIndex()
func (c *Menu) GetSelectedOption() *MenuOption {
idx := c.GetSelectedIndex()
if idx != -1 {
return &i.options[idx]
return &c.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]
func (c *Menu) GetOptionFromIndex(idx int) *MenuOption {
if idx >= 0 && idx < len(c.options) {
return &c.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]
func (c *Menu) GetOptionFromText(v string) *MenuOption {
for idx := range c.options {
testOption := &c.options[idx]
if testOption.GetText() == v {
return testOption
}
@ -135,9 +151,9 @@ func (i *Menu) GetOptionFromText(v string) *MenuOption {
// 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() {
func (c *Menu) GetSelectedIndex() int {
for idx := range c.options {
if c.options[idx].IsSelected() {
return idx
}
}
@ -145,267 +161,288 @@ func (i *Menu) GetSelectedIndex() int {
}
// SetSelectedIndex sets the selection to setIdx
func (i *Menu) SetSelectedIndex(idx int) {
if len(i.options) > 0 {
func (c *Menu) SetSelectedIndex(idx int) {
if len(c.options) > 0 {
if idx < 0 {
idx = 0
} else if idx >= len(i.options) {
idx = len(i.options) - 1
} else if idx >= len(c.options) {
idx = len(c.options) - 1
}
i.SetSelectedOption(&i.options[idx])
c.SetSelectedOption(&c.options[idx])
}
}
// SetSelectedOption sets the current selected option to v (if it's valid)
func (i *Menu) SetSelectedOption(v *MenuOption) {
for idx := range i.options {
if &i.options[idx] == v {
i.options[idx].Select()
func (c *Menu) SetSelectedOption(v *MenuOption) {
for idx := range c.options {
if &c.options[idx] == v {
c.options[idx].Select()
} else {
i.options[idx].Unselect()
c.options[idx].Unselect()
}
}
}
// SelectPrevOption Decrements the selected option (if it can)
func (i *Menu) SelectPrevOption() {
idx := i.GetSelectedIndex()
func (c *Menu) SelectPrevOption() {
idx := c.GetSelectedIndex()
for idx >= 0 {
idx--
testOption := i.GetOptionFromIndex(idx)
testOption := c.GetOptionFromIndex(idx)
if testOption != nil && !testOption.IsDisabled() {
i.SetSelectedOption(testOption)
c.SetSelectedOption(testOption)
return
}
}
}
// SelectNextOption Increments the selected option (if it can)
func (i *Menu) SelectNextOption() {
idx := i.GetSelectedIndex()
for idx < len(i.options) {
func (c *Menu) SelectNextOption() {
idx := c.GetSelectedIndex()
for idx < len(c.options) {
idx++
testOption := i.GetOptionFromIndex(idx)
testOption := c.GetOptionFromIndex(idx)
if testOption != nil && !testOption.IsDisabled() {
i.SetSelectedOption(testOption)
c.SetSelectedOption(testOption)
return
}
}
}
// SelectPageUpOption Goes up 'menu height' options
func (i *Menu) SelectPageUpOption() {
idx := i.GetSelectedIndex()
idx -= i.height
func (c *Menu) SelectPageUpOption() {
idx := c.GetSelectedIndex()
idx -= c.height
if idx < 0 {
idx = 0
}
i.SetSelectedIndex(idx)
c.SetSelectedIndex(idx)
return
}
// SelectPageDownOption Goes down 'menu height' options
func (i *Menu) SelectPageDownOption() {
idx := i.GetSelectedIndex()
idx += i.height
if idx >= len(i.options) {
idx = len(i.options) - 1
func (c *Menu) SelectPageDownOption() {
idx := c.GetSelectedIndex()
idx += c.height
if idx >= len(c.options) {
idx = len(c.options) - 1
}
i.SetSelectedIndex(idx)
c.SetSelectedIndex(idx)
return
}
// SelectFirstOption Goes to the top
func (i *Menu) SelectFirstOption() {
i.SetSelectedIndex(0)
func (c *Menu) SelectFirstOption() {
c.SetSelectedIndex(0)
return
}
// SelectLastOption Goes to the bottom
func (i *Menu) SelectLastOption() {
i.SetSelectedIndex(len(i.options) - 1)
func (c *Menu) SelectLastOption() {
c.SetSelectedIndex(len(c.options) - 1)
return
}
// SetOptionDisabled Disables the specified option
func (i *Menu) SetOptionDisabled(idx int) {
if len(i.options) > idx {
i.GetOptionFromIndex(idx).Disable()
func (c *Menu) SetOptionDisabled(idx int) {
if len(c.options) > idx {
c.GetOptionFromIndex(idx).Disable()
}
}
// SetOptionEnabled Enables the specified option
func (i *Menu) SetOptionEnabled(idx int) {
if len(i.options) > idx {
i.GetOptionFromIndex(idx).Enable()
func (c *Menu) SetOptionEnabled(idx int) {
if len(c.options) > idx {
c.GetOptionFromIndex(idx).Enable()
}
}
// HelpIsShown returns true or false if the help is displayed
func (i *Menu) HelpIsShown() bool { return i.showHelp }
func (c *Menu) HelpIsShown() bool { return c.showHelp }
// ShowHelp sets whether or not to display the help text
func (i *Menu) ShowHelp(b bool) {
i.showHelp = b
func (c *Menu) ShowHelp(b bool) {
c.showHelp = b
}
// GetFgColor returns the foreground color
func (i *Menu) GetFgColor() termbox.Attribute { return i.fg }
func (c *Menu) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *Menu) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *Menu) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *Menu) GetBgColor() termbox.Attribute { return i.bg }
func (c *Menu) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *Menu) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *Menu) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
// IsDone returns whether the user has answered the modal
func (i *Menu) IsDone() bool { return i.isDone }
func (c *Menu) IsDone() bool { return c.isDone }
// SetDone sets whether the modal has completed it's purpose
func (i *Menu) SetDone(b bool) {
i.isDone = b
func (c *Menu) SetDone(b bool) {
c.isDone = b
}
// IsBordered returns true or false if this menu has a border
func (i *Menu) IsBordered() bool { return i.bordered }
func (c *Menu) IsBordered() bool { return c.bordered }
// SetBordered sets whether we render a border around the menu
func (i *Menu) SetBordered(b bool) {
i.bordered = b
func (c *Menu) SetBordered(b bool) {
c.bordered = b
}
// EnableVimMode Enables h,j,k,l navigation
func (i *Menu) EnableVimMode() {
i.vimMode = true
func (c *Menu) EnableVimMode() {
c.vimMode = true
}
// DisableVimMode Disables h,j,k,l navigation
func (i *Menu) DisableVimMode() {
i.vimMode = false
func (c *Menu) DisableVimMode() {
c.vimMode = false
}
// HandleEvent handles the termbox event and returns whether it was consumed
func (i *Menu) HandleEvent(event termbox.Event) bool {
func (c *Menu) HandleEvent(event termbox.Event) bool {
if event.Key == termbox.KeyEnter || event.Key == termbox.KeySpace {
i.isDone = true
c.isDone = true
return true
}
currentIdx := i.GetSelectedIndex()
currentIdx := c.GetSelectedIndex()
switch event.Key {
case termbox.KeyArrowUp:
i.SelectPrevOption()
c.SelectPrevOption()
case termbox.KeyArrowDown:
i.SelectNextOption()
c.SelectNextOption()
case termbox.KeyArrowLeft:
i.SelectPageUpOption()
c.SelectPageUpOption()
case termbox.KeyArrowRight:
i.SelectPageDownOption()
c.SelectPageDownOption()
}
if i.vimMode {
if c.vimMode {
switch event.Ch {
case 'j':
i.SelectNextOption()
c.SelectNextOption()
case 'k':
i.SelectPrevOption()
c.SelectPrevOption()
}
if event.Key == termbox.KeyCtrlF {
i.SelectPageDownOption()
c.SelectPageDownOption()
} else if event.Key == termbox.KeyCtrlB {
i.SelectPageUpOption()
c.SelectPageUpOption()
}
}
if i.GetSelectedIndex() != currentIdx {
if c.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)
func (c *Menu) Draw() {
useFg, useBg := c.fg, c.bg
if c.active {
useFg, useBg = c.activeFg, c.activeBg
}
if i.bordered {
if i.height == -1 {
DrawBorder(i.x, i.y, i.x+i.width, i.y+optionHeight+1, i.fg, i.bg)
// First blank out the area we'll be putting the menu
FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, useFg, useBg)
// Now draw the border
optionStartX := c.x
optionStartY := c.y
optionWidth := c.width
_ = optionWidth
optionHeight := c.height
if optionHeight == -1 {
optionHeight = len(c.options)
}
if c.bordered {
pct := float64(c.GetSelectedIndex()) / float64(len(c.options))
if c.title == "" {
if len(c.options) > c.height-2 {
DrawBorderWithPct(c.x, c.y, c.x+c.width, c.y+c.height, pct, useFg, useBg)
} else {
DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, useFg, useBg)
}
} else {
DrawBorder(i.x, i.y, i.x+i.width, i.y+optionHeight, i.fg, i.bg)
if len(c.options) > c.height-2 {
DrawBorderWithTitleAndPct(c.x, c.y, c.x+c.width, c.y+c.height, " "+c.title+" ", pct, useFg, useBg)
} else {
DrawBorderWithTitle(c.x, c.y, c.x+c.width, c.y+c.height, " "+c.title+" ", useFg, useBg)
}
}
optionStartX = i.x + 1
optionStartY = i.y + 1
optionWidth = i.width - 1
optionStartX = c.x + 1
optionStartY = c.y + 1
optionWidth = c.width - 1
optionHeight -= 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++
optionHeight--
if len(c.options) > 0 {
firstDispIdx := 0
lastDispIdx := len(c.options) - 1
if len(c.options) > c.height-2 {
lastDispIdx = c.height - 2
}
optionHeight--
}
if len(i.options) > 0 {
// If the currently selected option is disabled, move to the next
if i.GetSelectedOption().IsDisabled() {
i.SelectNextOption()
if c.GetSelectedIndex() > c.height-2 {
firstDispIdx = c.GetSelectedIndex() - (c.height - 2)
lastDispIdx = c.GetSelectedIndex()
}
// Print the options
bldHeight := (optionHeight / 2)
startIdx := i.GetSelectedIndex()
endIdx := i.GetSelectedIndex()
for bldHeight > 0 && startIdx >= 1 {
startIdx--
bldHeight--
}
bldHeight += (optionHeight / 2)
for bldHeight > 0 && endIdx < len(i.options) {
endIdx++
bldHeight--
}
for idx := startIdx; idx < endIdx; idx++ { //i.options {
if i.GetSelectedIndex()-idx >= optionHeight-1 {
// Skip this one
continue
}
currOpt := &i.options[idx]
for idx := firstDispIdx; idx < lastDispIdx; idx++ {
currOpt := &c.options[idx]
outTxt := currOpt.GetText()
if len(outTxt) >= i.width {
outTxt = outTxt[:i.width]
}
if currOpt.IsDisabled() {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.disabledFg, i.disabledBg)
} else if i.GetSelectedOption() == currOpt {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.selectedFg, i.selectedBg)
DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.disabledFg, c.disabledBg)
} else if c.GetSelectedOption() == currOpt {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.selectedFg, c.selectedBg)
} else {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.fg, i.bg)
DrawStringAtPoint(outTxt, optionStartX, optionStartY, useFg, useBg)
}
optionStartY++
if optionStartY > i.y+optionHeight-1 {
break
}
}
}
/*
// Print the options
bldHeight := (optionHeight / 2)
startIdx := c.GetSelectedIndex()
endIdx := c.GetSelectedIndex()
for bldHeight > 0 && startIdx >= 1 {
startIdx--
bldHeight--
}
bldHeight += (optionHeight / 2)
for bldHeight > 0 && endIdx < len(c.options) {
endIdx++
bldHeight--
}
for idx := startIdx; idx < endIdx; idx++ {
if c.GetSelectedIndex()-idx >= optionHeight-1 {
// Skip this one
continue
}
currOpt := &c.options[idx]
outTxt := currOpt.GetText()
if len(outTxt) >= c.width {
outTxt = outTxt[:c.width]
}
if currOpt.IsDisabled() {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.disabledFg, c.disabledBg)
} else if c.GetSelectedOption() == currOpt {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.selectedFg, c.selectedBg)
} else {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, useFg, useBg)
}
optionStartY++
if optionStartY > c.y+optionHeight-1 {
break
}
}
}
*/
}
/* MenuOption Struct & methods */
@ -427,52 +464,52 @@ func CreateOptionFromText(s string) *MenuOption {
}
// SetText Sets the text for this option
func (i *MenuOption) SetText(s string) {
i.text = s
func (c *MenuOption) SetText(s string) {
c.text = s
}
// GetText Returns the text for this option
func (i *MenuOption) GetText() string { return i.text }
func (c *MenuOption) GetText() string { return c.text }
// Disable Sets this option to disabled
func (i *MenuOption) Disable() {
i.disabled = true
func (c *MenuOption) Disable() {
c.disabled = true
}
// Enable Sets this option to enabled
func (i *MenuOption) Enable() {
i.disabled = false
func (c *MenuOption) Enable() {
c.disabled = false
}
// IsDisabled returns whether this option is enabled
func (i *MenuOption) IsDisabled() bool {
return i.disabled
func (c *MenuOption) IsDisabled() bool {
return c.disabled
}
// IsSelected Returns whether this option is selected
func (i *MenuOption) IsSelected() bool {
return i.selected
func (c *MenuOption) IsSelected() bool {
return c.selected
}
// Select Sets this option to selected
func (i *MenuOption) Select() {
i.selected = true
func (c *MenuOption) Select() {
c.selected = true
}
// Unselect Sets this option to not selected
func (i *MenuOption) Unselect() {
i.selected = false
func (c *MenuOption) Unselect() {
c.selected = false
}
// SetHelpText Sets this option's help text to s
func (i *MenuOption) SetHelpText(s string) {
i.helpText = s
func (c *MenuOption) SetHelpText(s string) {
c.helpText = s
}
// GetHelpText Returns the help text for this option
func (i *MenuOption) GetHelpText() string { return i.helpText }
func (c *MenuOption) GetHelpText() string { return c.helpText }
// AddToSubMenu adds a slice of MenuOptions to this option
func (i *MenuOption) AddToSubMenu(sub *MenuOption) {
i.subMenu = append(i.subMenu, *sub)
func (c *MenuOption) AddToSubMenu(sub *MenuOption) {
c.subMenu = append(c.subMenu, *sub)
}

View File

@ -15,157 +15,165 @@ type ProgressBar struct {
alignment TextAlignment
colorized bool
x, y int
width, height int
bg, fg termbox.Attribute
x, y int
width, height int
fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
active bool
}
// CreateProgressBar Create a progress bar object
func CreateProgressBar(tot, x, y int, fg, bg termbox.Attribute) *ProgressBar {
i := ProgressBar{total: tot,
c := ProgressBar{total: tot,
fullChar: '#', emptyChar: ' ',
x: x, y: y, height: 1, width: 10,
bordered: true, fg: fg, bg: bg,
activeFg: fg, activeBg: bg,
alignment: AlignLeft,
}
return &i
return &c
}
func (c *ProgressBar) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg }
func (c *ProgressBar) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg }
func (c *ProgressBar) SetActive(a bool) { c.active = a }
func (c *ProgressBar) IsActive() bool { return c.active }
// GetID returns this control's ID
func (i *ProgressBar) GetID() string { return i.id }
func (c *ProgressBar) GetID() string { return c.id }
// SetID sets this control's ID
func (i *ProgressBar) SetID(newID string) {
i.id = newID
func (c *ProgressBar) SetID(newID string) {
c.id = newID
}
// GetProgress returns the curret progress value
func (i *ProgressBar) GetProgress() int {
return i.progress
func (c *ProgressBar) GetProgress() int {
return c.progress
}
// SetProgress sets the current progress of the bar
func (i *ProgressBar) SetProgress(p int) {
if (p <= i.total || i.allowOverflow) || (p >= 0 || i.allowUnderflow) {
i.progress = p
func (c *ProgressBar) SetProgress(p int) {
if (p <= c.total || c.allowOverflow) || (p >= 0 || c.allowUnderflow) {
c.progress = p
}
}
// IncrProgress increments the current progress of the bar
func (i *ProgressBar) IncrProgress() {
if i.progress < i.total || i.allowOverflow {
i.progress++
func (c *ProgressBar) IncrProgress() {
if c.progress < c.total || c.allowOverflow {
c.progress++
}
}
// DecrProgress decrements the current progress of the bar
func (i *ProgressBar) DecrProgress() {
if i.progress > 0 || i.allowUnderflow {
i.progress--
func (c *ProgressBar) DecrProgress() {
if c.progress > 0 || c.allowUnderflow {
c.progress--
}
}
// GetPercent returns the percent full of the bar
func (i *ProgressBar) GetPercent() int {
return int(float64(i.progress) / float64(i.total) * 100)
func (c *ProgressBar) GetPercent() int {
return int(float64(c.progress) / float64(c.total) * 100)
}
// EnableOverflow Tells the progress bar that it can go over the total
func (i *ProgressBar) EnableOverflow() {
i.allowOverflow = true
func (c *ProgressBar) EnableOverflow() {
c.allowOverflow = true
}
// DisableOverflow Tells the progress bar that it can NOT go over the total
func (i *ProgressBar) DisableOverflow() {
i.allowOverflow = false
func (c *ProgressBar) DisableOverflow() {
c.allowOverflow = false
}
// EnableUnderflow Tells the progress bar that it can go below zero
func (i *ProgressBar) EnableUnderflow() {
i.allowUnderflow = true
func (c *ProgressBar) EnableUnderflow() {
c.allowUnderflow = true
}
// DisableUnderflow Tells the progress bar that it can NOT go below zero
func (i *ProgressBar) DisableUnderflow() {
i.allowUnderflow = false
func (c *ProgressBar) DisableUnderflow() {
c.allowUnderflow = false
}
// GetFullChar returns the rune used for 'full'
func (i *ProgressBar) GetFullChar() rune {
return i.fullChar
func (c *ProgressBar) GetFullChar() rune {
return c.fullChar
}
// SetFullChar sets the rune used for 'full'
func (i *ProgressBar) SetFullChar(f rune) {
i.fullChar = f
func (c *ProgressBar) SetFullChar(f rune) {
c.fullChar = f
}
// GetEmptyChar gets the rune used for 'empty'
func (i *ProgressBar) GetEmptyChar() rune {
return i.emptyChar
func (c *ProgressBar) GetEmptyChar() rune {
return c.emptyChar
}
// SetEmptyChar sets the rune used for 'empty'
func (i *ProgressBar) SetEmptyChar(f rune) {
i.emptyChar = f
func (c *ProgressBar) SetEmptyChar(f rune) {
c.emptyChar = f
}
// GetX Return the x position of the Progress Bar
func (i *ProgressBar) GetX() int { return i.x }
func (c *ProgressBar) GetX() int { return c.x }
// SetX set the x position of the ProgressBar to x
func (i *ProgressBar) SetX(x int) {
i.x = x
func (c *ProgressBar) SetX(x int) {
c.x = x
}
// GetY Return the y position of the ProgressBar
func (i *ProgressBar) GetY() int { return i.y }
func (c *ProgressBar) GetY() int { return c.y }
// SetY Set the y position of the ProgressBar to y
func (i *ProgressBar) SetY(y int) {
i.y = y
func (c *ProgressBar) SetY(y int) {
c.y = y
}
// GetHeight returns the height of the progress bar
// Defaults to 1 (3 if bordered)
func (i *ProgressBar) GetHeight() int {
return i.height
func (c *ProgressBar) GetHeight() int {
return c.height
}
// SetHeight Sets the height of the progress bar
func (i *ProgressBar) SetHeight(h int) {
i.height = h
func (c *ProgressBar) SetHeight(h int) {
c.height = h
}
// GetWidth returns the width of the progress bar
func (i *ProgressBar) GetWidth() int {
return i.width
func (c *ProgressBar) GetWidth() int {
return c.width
}
// SetWidth Sets the width of the progress bar
func (i *ProgressBar) SetWidth(w int) {
i.width = w
func (c *ProgressBar) SetWidth(w int) {
c.width = w
}
// GetFgColor returns the foreground color
func (i *ProgressBar) GetFgColor() termbox.Attribute { return i.fg }
func (c *ProgressBar) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *ProgressBar) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *ProgressBar) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *ProgressBar) GetBgColor() termbox.Attribute { return i.bg }
func (c *ProgressBar) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *ProgressBar) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *ProgressBar) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
// Align Tells which direction the progress bar empties
func (i *ProgressBar) Align(a TextAlignment) {
i.alignment = a
func (c *ProgressBar) Align(a TextAlignment) {
c.alignment = a
}
// SetColorized sets whether the progress bar should be colored
@ -173,48 +181,48 @@ func (i *ProgressBar) Align(a TextAlignment) {
// 10% - Red
// 50% - Yellow
// 80% - Green
func (i *ProgressBar) SetColorized(c bool) {
i.colorized = c
func (c *ProgressBar) SetColorized(color bool) {
c.colorized = color
}
// HandleEvent accepts the termbox event and returns whether it was consumed
func (i *ProgressBar) HandleEvent(event termbox.Event) bool {
func (c *ProgressBar) HandleEvent(event termbox.Event) bool {
return false
}
// Draw outputs the input field on the screen
func (i *ProgressBar) Draw() {
func (c *ProgressBar) Draw() {
// For now, just draw a [#### ] bar
// TODO: make this more advanced
useFg := i.fg
if i.colorized {
if i.GetPercent() < 10 {
useFg := c.fg
if c.colorized {
if c.GetPercent() < 10 {
useFg = termbox.ColorRed
} else if i.GetPercent() < 50 {
} else if c.GetPercent() < 50 {
useFg = termbox.ColorYellow
} else {
useFg = termbox.ColorGreen
}
}
drawX, drawY := i.x, i.y
fillWidth, fillHeight := i.width-2, i.height
DrawStringAtPoint("[", drawX, drawY, i.fg, i.bg)
numFull := int(float64(fillWidth) * float64(i.progress) / float64(i.total))
FillWithChar(i.fullChar, drawX+1, drawY, drawX+1+numFull, drawY+(fillHeight-1), useFg, i.bg)
DrawStringAtPoint("]", drawX+i.width-1, drawY, i.fg, i.bg)
drawX, drawY := c.x, c.y
fillWidth, fillHeight := c.width-2, c.height
DrawStringAtPoint("[", drawX, drawY, c.fg, c.bg)
numFull := int(float64(fillWidth) * float64(c.progress) / float64(c.total))
FillWithChar(c.fullChar, drawX+1, drawY, drawX+1+numFull, drawY+(fillHeight-1), useFg, c.bg)
DrawStringAtPoint("]", drawX+c.width-1, drawY, c.fg, c.bg)
/*
drawX, drawY := i.x, i.y
drawWidth, drawHeight := i.width, i.height
if i.bordered {
if i.height == 1 && i.width > 2 {
drawX, drawY := c.x, c.y
drawWidth, drawHeight := c.width, c.height
if c.bordered {
if c.height == 1 && c.width > 2 {
// Just using [ & ] for the border
DrawStringAtPoint("[", drawX, drawY, i.fg, i.bg)
DrawStringAtPoint("]", drawX+i.width-1, drawY, i.fg, i.bg)
DrawStringAtPoint("[", drawX, drawY, c.fg, c.bg)
DrawStringAtPoint("]", drawX+c.width-1, drawY, c.fg, c.bg)
drawX++
drawWidth -= 2
} else if i.height >= 3 {
DrawBorder(drawX, drawY, drawX+i.width, drawY+i.height, i.fg, i.bg)
} else if c.height >= 3 {
DrawBorder(drawX, drawY, drawX+c.width, drawY+c.height, c.fg, c.bg)
drawX++
drawY++
drawWidth -= 2
@ -223,14 +231,14 @@ func (i *ProgressBar) Draw() {
}
// Figure out how many chars are full
numFull := drawWidth * (i.progress / i.total)
switch i.alignment {
numFull := drawWidth * (c.progress / c.total)
switch c.alignment {
case AlignRight: // TODO: Fill from right to left
case AlignCenter: // TODO: Fill from middle out
default: // Fill from left to right
FillWithChar(i.fullChar, drawX, drawY, drawX+numFull, drawY+(drawHeight-1), i.fg, i.bg)
FillWithChar(c.fullChar, drawX, drawY, drawX+numFull, drawY+(drawHeight-1), c.fg, c.bg)
if numFull < drawWidth {
FillWithChar(i.emptyChar, drawX+numFull, drawY, drawX+drawWidth-1, drawY+(drawHeight-1), i.fg, i.bg)
FillWithChar(c.emptyChar, drawX+numFull, drawY, drawX+drawWidth-1, drawY+(drawHeight-1), c.fg, c.bg)
}
}
*/

View File

@ -10,126 +10,131 @@ type ScrollFrame struct {
scrollX, scrollY int
tabIdx int
fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool
controls []termboxControl
active bool
}
// CreateScrollFrame creates Scrolling Frame at x, y that is w by h
func CreateScrollFrame(x, y, w, h int, fg, bg termbox.Attribute) *ScrollFrame {
s := ScrollFrame{x: x, y: y, width: w, height: h, fg: fg, bg: bg}
return &s
c := ScrollFrame{
x: x, y: y, width: w, height: h,
fg: fg, bg: bg, activeFg: fg, activeBg: bg,
}
return &c
}
// GetID returns this control's ID
func (i *ScrollFrame) GetID() string { return i.id }
func (c *ScrollFrame) GetID() string { return c.id }
// SetID sets this control's ID
func (i *ScrollFrame) SetID(newID string) {
i.id = newID
func (c *ScrollFrame) SetID(newID string) {
c.id = newID
}
// GetX returns the x position of the scroll frame
func (i *ScrollFrame) GetX() int { return i.x }
func (c *ScrollFrame) GetX() int { return c.x }
// SetX sets the x position of the scroll frame
func (i *ScrollFrame) SetX(x int) {
i.x = x
func (c *ScrollFrame) SetX(x int) {
c.x = x
}
// GetY returns the y position of the scroll frame
func (i *ScrollFrame) GetY() int { return i.y }
func (c *ScrollFrame) GetY() int { return c.y }
// SetY sets the y position of the scroll frame
func (i *ScrollFrame) SetY(y int) {
i.y = y
func (c *ScrollFrame) SetY(y int) {
c.y = y
}
// GetWidth returns the current width of the scroll frame
func (i *ScrollFrame) GetWidth() int { return i.width }
func (c *ScrollFrame) GetWidth() int { return c.width }
// SetWidth sets the current width of the scroll frame
func (i *ScrollFrame) SetWidth(w int) {
i.width = w
func (c *ScrollFrame) SetWidth(w int) {
c.width = w
}
// GetHeight returns the current height of the scroll frame
func (i *ScrollFrame) GetHeight() int { return i.height }
func (c *ScrollFrame) GetHeight() int { return c.height }
// SetHeight sets the current height of the scroll frame
func (i *ScrollFrame) SetHeight(h int) {
i.height = h
func (c *ScrollFrame) SetHeight(h int) {
c.height = h
}
// GetFgColor returns the foreground color
func (i *ScrollFrame) GetFgColor() termbox.Attribute { return i.fg }
func (c *ScrollFrame) GetFgColor() termbox.Attribute { return c.fg }
// SetFgColor sets the foreground color
func (i *ScrollFrame) SetFgColor(fg termbox.Attribute) {
i.fg = fg
func (c *ScrollFrame) SetFgColor(fg termbox.Attribute) {
c.fg = fg
}
// GetBgColor returns the background color
func (i *ScrollFrame) GetBgColor() termbox.Attribute { return i.bg }
func (c *ScrollFrame) GetBgColor() termbox.Attribute { return c.bg }
// SetBgColor sets the current background color
func (i *ScrollFrame) SetBgColor(bg termbox.Attribute) {
i.bg = bg
func (c *ScrollFrame) SetBgColor(bg termbox.Attribute) {
c.bg = bg
}
// IsBordered returns true or false if this scroll frame has a border
func (i *ScrollFrame) IsBordered() bool { return i.bordered }
func (c *ScrollFrame) IsBordered() bool { return c.bordered }
// SetBordered sets whether we render a border around the scroll frame
func (i *ScrollFrame) SetBordered(b bool) {
i.bordered = b
func (c *ScrollFrame) SetBordered(b bool) {
c.bordered = b
}
// GetScrollX returns the x distance scrolled
func (i *ScrollFrame) GetScrollX() int {
return i.scrollX
func (c *ScrollFrame) GetScrollX() int {
return c.scrollX
}
// GetScrollY returns the y distance scrolled
func (i *ScrollFrame) GetScrollY() int {
return i.scrollY
func (c *ScrollFrame) GetScrollY() int {
return c.scrollY
}
// ScrollDown scrolls the frame down
func (i *ScrollFrame) ScrollDown() {
i.scrollY++
func (c *ScrollFrame) ScrollDown() {
c.scrollY++
}
// ScrollUp scrolls the frame up
func (i *ScrollFrame) ScrollUp() {
if i.scrollY > 0 {
i.scrollY--
func (c *ScrollFrame) ScrollUp() {
if c.scrollY > 0 {
c.scrollY--
}
}
// ScrollLeft scrolls the frame left
func (i *ScrollFrame) ScrollLeft() {
if i.scrollX > 0 {
i.scrollX--
func (c *ScrollFrame) ScrollLeft() {
if c.scrollX > 0 {
c.scrollX--
}
}
// ScrollRight scrolls the frame right
func (i *ScrollFrame) ScrollRight() {
i.scrollX++
func (c *ScrollFrame) ScrollRight() {
c.scrollX++
}
// AddControl adds a control to the frame
func (i *ScrollFrame) AddControl(t termboxControl) {
i.controls = append(i.controls, t)
func (c *ScrollFrame) AddControl(t termboxControl) {
c.controls = append(c.controls, t)
}
// DrawControl figures out the relative position of the control,
// sets it, draws it, then resets it.
func (i *ScrollFrame) DrawControl(t termboxControl) {
if i.IsVisible(t) {
func (c *ScrollFrame) DrawControl(t termboxControl) {
if c.IsVisible(t) {
ctlX, ctlY := t.GetX(), t.GetY()
t.SetX((i.GetX() + ctlX))
t.SetY((i.GetY() + ctlY))
t.SetX((c.GetX() + ctlX))
t.SetY((c.GetY() + ctlY))
t.Draw()
t.SetX(ctlX)
t.SetY(ctlY)
@ -138,35 +143,35 @@ func (i *ScrollFrame) DrawControl(t termboxControl) {
// IsVisible takes a Termbox Control and returns whether
// that control would be visible in the frame
func (i *ScrollFrame) IsVisible(t termboxControl) bool {
func (c *ScrollFrame) IsVisible(t termboxControl) bool {
// Check if any part of t should be visible
cX, cY := t.GetX(), t.GetY()
if cX+t.GetWidth() >= i.scrollX && cX <= i.scrollX+i.width {
return cY+t.GetHeight() >= i.scrollY && cY <= i.scrollY+i.height
if cX+t.GetWidth() >= c.scrollX && cX <= c.scrollX+c.width {
return cY+t.GetHeight() >= c.scrollY && cY <= c.scrollY+c.height
}
return false
}
// HandleEvent accepts the termbox event and returns whether it was consumed
func (i *ScrollFrame) HandleEvent(event termbox.Event) bool {
func (c *ScrollFrame) HandleEvent(event termbox.Event) bool {
return false
}
// DrawToStrings generates a slice of strings with what should
// be drawn to the screen
func (i *ScrollFrame) DrawToStrings() []string {
func (c *ScrollFrame) DrawToStrings() []string {
return []string{}
}
// Draw outputs the Scoll Frame on the screen
func (i *ScrollFrame) Draw() {
maxWidth := i.width
maxHeight := i.height
x, y := i.x, i.y
startX := i.x
startY := i.y
if i.bordered {
DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg)
func (c *ScrollFrame) Draw() {
maxWidth := c.width
maxHeight := c.height
x, y := c.x, c.y
startX := c.x
startY := c.y
if c.bordered {
DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg)
maxWidth--
maxHeight--
x++
@ -174,7 +179,7 @@ func (i *ScrollFrame) Draw() {
startX++
startY++
}
for idx := range i.controls {
i.DrawControl(i.controls[idx])
for idx := range c.controls {
c.DrawControl(c.controls[idx])
}
}

View File

@ -1,6 +1,7 @@
package termboxUtil
import (
"errors"
"fmt"
"strings"
@ -27,6 +28,10 @@ type termboxControl interface {
SetTabSkip(bool)
IsTabSkipped() bool
Draw()
SetActive(bool)
IsActive() bool
SetActiveFgColor(termbox.Attribute)
SetActiveBgColor(termbox.Attribute)
}
// TextAlignment is an int value for how we're aligning text
@ -106,17 +111,67 @@ 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)
func DrawBorder(x1, y1, x2, y2 int, fg, bg termbox.Attribute) {
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)
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)
}
func DrawBorderWithPct(x1, y1, x2, y2 int, pct float64, fg, bg termbox.Attribute) {
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)
// Now the percent indicator
pctY := int(((float64(y2)-float64(y1)-2)*pct)+float64(y1)) + 1
termbox.SetCell(x2, pctY, '▒', fg, bg)
termbox.SetCell(x1, y2, '╚', fg, bg)
FillWithChar('═', x1+1, y2, x2-1, y2, fg, bg)
termbox.SetCell(x2, y2, '╝', fg, bg)
}
func DrawBorderWithTitle(x1, y1, x2, y2 int, title string, fg, bg termbox.Attribute) {
termbox.SetCell(x1, y1, '╔', fg, bg)
DrawStringAtPoint(title, x1+1, y1, fg, bg)
FillWithChar('═', x1+len(title)+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)
}
func DrawBorderWithTitleAndPct(x1, y1, x2, y2 int, title string, pct float64, fg, bg termbox.Attribute) {
termbox.SetCell(x1, y1, '╔', fg, bg)
DrawStringAtPoint(title, x1+1, y1, fg, bg)
FillWithChar('═', x1+len(title)+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)
// Now the percent indicator
pctY := int(((float64(y2)-float64(y1)-2)*pct)+float64(y1)) + 1
termbox.SetCell(x2, pctY, '▒', 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
@ -148,4 +203,19 @@ func AlignTextWithFill(txt string, width int, align TextAlignment, fill rune) st
}
}
func ToLabel(c termboxControl) (*Label, error) {
v, ok := c.(*Label)
if ok {
return v, nil
}
return nil, errors.New("Control isn't a Label")
}
func ToInputField(c termboxControl) (*InputField, error) {
v, ok := c.(*InputField)
if ok {
return v, nil
}
return nil, errors.New("Control isn't an Input Field")
}
/* More advanced things are in their respective files */