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" "strconv"
"syscall" "syscall"
"gogs.bullercodeworks.com/brian/termbox-util" "github.com/br0xen/termbox-util"
"github.com/nsf/termbox-go" "github.com/nsf/termbox-go"
) )
@ -115,4 +115,4 @@ func handleEvent(event termbox.Event) bool {
} }
frame.GetActiveControl().SetFgColor(termbox.ColorGreen) frame.GetActiveControl().SetFgColor(termbox.ColorGreen)
return true return true
} }

View File

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

View File

@ -8,17 +8,20 @@ import (
// ASCIIArt is a []string with more functions // ASCIIArt is a []string with more functions
type ASCIIArt struct { type ASCIIArt struct {
id string id string
contents []string contents []string
x, y int x, y int
bg, fg termbox.Attribute bg, fg termbox.Attribute
bordered bool activeFg, activeBg termbox.Attribute
tabSkip bool bordered bool
tabSkip bool
active bool
} }
// CreateASCIIArt Create an ASCII art object from a string slice // CreateASCIIArt Create an ASCII art object from a string slice
func CreateASCIIArt(c []string, x, y int, fg, bg termbox.Attribute) *ASCIIArt { 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 := ASCIIArt{contents: c, x: x, y: y, fg: fg, bg: bg, bordered: false, tabSkip: true}
i.activeFg, i.activeBg = fg, bg
return &i 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 // SetContents Sets the contents of i to c
func (i *ASCIIArt) SetContents(c []string) { func (i *ASCIIArt) SetContents(c []string) {
i.contents = c 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 x, y, width, height int
showHelp bool showHelp bool
cursor int cursor int
bg, fg termbox.Attribute fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
isDone bool isDone bool
accepted bool accepted bool
value string value string
@ -23,7 +24,7 @@ type ConfirmModal struct {
// CreateConfirmModal Creates a confirmation modal with the specified attributes // CreateConfirmModal Creates a confirmation modal with the specified attributes
func CreateConfirmModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *ConfirmModal { 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 == "" { if i.title == "" && i.text == "" {
i.title = "Confirm?" 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 // DropMenu is a title that, when active drops a menu down
type DropMenu struct { type DropMenu struct {
id string id string
title string title string
x, y, width, height int x, y, width, height int
bg, fg termbox.Attribute bg, fg termbox.Attribute
selectedBg, selectedFg termbox.Attribute activeFg, activeBg termbox.Attribute
menu *Menu cursorBg, cursorFg termbox.Attribute
menuSelected bool menu *Menu
showMenu bool menuSelected bool
bordered bool showMenu bool
tabSkip bool bordered bool
tabSkip bool
active bool
} }
// CreateDropMenu Creates a menu with the specified attributes // 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 { func CreateDropMenu(title string, options []string, x, y, width, height int, fg, bg, cursorFg, cursorBg termbox.Attribute) *DropMenu {
i := DropMenu{ c := DropMenu{
title: title, title: title,
x: x, y: y, width: width, height: height, x: x, y: y, width: width, height: height,
fg: fg, bg: bg, fg: fg, bg: bg, activeFg: fg, activeBg: bg,
selectedFg: fg, selectedBg: bg, cursorFg: fg, cursorBg: bg,
} }
i.menu = CreateMenu("", options, x, y+2, width, height, fg, bg) c.menu = CreateMenu("", options, x, y+2, width, height, fg, bg)
return &i return &c
} }
// GetID returns this control's ID // 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 // SetID sets this control's ID
func (i *DropMenu) SetID(newID string) { func (c *DropMenu) SetID(newID string) {
i.id = newID 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 // 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 // SetTitle sets the current title of the menu to s
func (i *DropMenu) SetTitle(s string) { func (c *DropMenu) SetTitle(s string) {
i.title = s c.title = s
} }
// GetMenu returns the menu for this dropmenu // GetMenu returns the menu for this dropmenu
func (i *DropMenu) GetMenu() *Menu { func (c *DropMenu) GetMenu() *Menu {
return i.menu return c.menu
} }
// GetX returns the current x coordinate of the 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 // SetX sets the current x coordinate of the menu to x
func (i *DropMenu) SetX(x int) { func (c *DropMenu) SetX(x int) {
i.x = x c.x = x
} }
// GetY returns the current y coordinate of the menu // 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 // SetY sets the current y coordinate of the menu to y
func (i *DropMenu) SetY(y int) { func (c *DropMenu) SetY(y int) {
i.y = y c.y = y
} }
// GetWidth returns the current width of the menu // 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 // SetWidth sets the current menu width to width
func (i *DropMenu) SetWidth(width int) { func (c *DropMenu) SetWidth(width int) {
i.width = width c.width = width
} }
// GetHeight returns the current height of the menu // 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 // SetHeight set the height of the menu to height
func (i *DropMenu) SetHeight(height int) { func (c *DropMenu) SetHeight(height int) {
i.height = height c.height = height
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *DropMenu) SetFgColor(fg termbox.Attribute) { func (c *DropMenu) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *DropMenu) SetBgColor(bg termbox.Attribute) { func (c *DropMenu) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
// IsBordered returns the bordered flag // 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 // SetBordered sets the bordered flag
func (i *DropMenu) SetBordered(b bool) { func (c *DropMenu) SetBordered(b bool) {
i.bordered = b c.bordered = b
i.menu.SetBordered(b) c.menu.SetBordered(b)
} }
// IsDone returns whether the user has answered the modal // 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 // SetDone sets whether the modal has completed it's purpose
func (i *DropMenu) SetDone(b bool) { func (c *DropMenu) SetDone(b bool) {
i.menu.isDone = b c.menu.isDone = b
} }
// IsTabSkipped returns whether this modal has it's tabskip flag set // IsTabSkipped returns whether this modal has it's tabskip flag set
func (i *DropMenu) IsTabSkipped() bool { func (c *DropMenu) IsTabSkipped() bool {
return i.tabSkip return c.tabSkip
} }
// SetTabSkip sets the tabskip flag for this control // SetTabSkip sets the tabskip flag for this control
func (i *DropMenu) SetTabSkip(b bool) { func (c *DropMenu) SetTabSkip(b bool) {
i.tabSkip = b c.tabSkip = b
} }
// ShowMenu tells the menu to draw the options // ShowMenu tells the menu to draw the options
func (i *DropMenu) ShowMenu() { func (c *DropMenu) ShowMenu() {
i.showMenu = true c.showMenu = true
i.menuSelected = true c.menuSelected = true
} }
// HideMenu tells the menu to hide the options // HideMenu tells the menu to hide the options
func (i *DropMenu) HideMenu() { func (c *DropMenu) HideMenu() {
i.showMenu = false c.showMenu = false
i.menuSelected = false c.menuSelected = false
} }
// HandleEvent handles the termbox event and returns whether it was consumed // HandleEvent handles the termbox event and returns whether it was consumed
func (i *DropMenu) HandleEvent(event termbox.Event) bool { func (c *DropMenu) HandleEvent(event termbox.Event) bool {
moveUp := (event.Key == termbox.KeyArrowUp || (i.menu.vimMode && event.Ch == 'k')) moveUp := (event.Key == termbox.KeyArrowUp || (c.menu.vimMode && event.Ch == 'k'))
moveDown := (event.Key == termbox.KeyArrowDown || (i.menu.vimMode && event.Ch == 'j')) moveDown := (event.Key == termbox.KeyArrowDown || (c.menu.vimMode && event.Ch == 'j'))
if i.menuSelected { if c.menuSelected {
selIdx := i.menu.GetSelectedIndex() selIdx := c.menu.GetSelectedIndex()
if (moveUp && selIdx == 0) || (moveDown && selIdx == (len(i.menu.options)-1)) { if (moveUp && selIdx == 0) || (moveDown && selIdx == (len(c.menu.options)-1)) {
i.menuSelected = false c.menuSelected = false
} else { } else {
if i.menu.HandleEvent(event) { if c.menu.HandleEvent(event) {
if i.menu.IsDone() { if c.menu.IsDone() {
i.HideMenu() c.HideMenu()
} }
return true return true
} }
} }
} else { } else {
i.ShowMenu() c.ShowMenu()
return true return true
} }
return false return false
} }
// Draw draws the menu // Draw draws the menu
func (i *DropMenu) Draw() { func (c *DropMenu) Draw() {
// The title // The title
ttlFg, ttlBg := i.fg, i.bg ttlFg, ttlBg := c.fg, c.bg
if !i.menuSelected { if !c.menuSelected {
ttlFg, ttlBg = i.selectedFg, i.selectedBg ttlFg, ttlBg = c.cursorFg, c.cursorBg
} }
ttlTxt := i.title ttlTxt := c.title
if i.showMenu { if c.showMenu {
ttlTxt = ttlTxt + "-Showing Menu" ttlTxt = ttlTxt + "-Showing Menu"
} }
DrawStringAtPoint(AlignText(i.title, i.width, AlignLeft), i.x, i.y, ttlFg, ttlBg) DrawStringAtPoint(AlignText(c.title, c.width, AlignLeft), c.x, c.y, ttlFg, ttlBg)
if i.showMenu { if c.showMenu {
i.menu.Draw() c.menu.Draw()
} }
} }

View File

@ -9,187 +9,252 @@ type Frame struct {
x, y, width, height int x, y, width, height int
tabIdx int tabIdx int
fg, bg termbox.Attribute fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool bordered bool
controls []termboxControl controls []termboxControl
tabSkip bool tabSkip bool
active bool
title string
} }
// CreateFrame creates a Frame at x, y that is w by h // CreateFrame creates a Frame at x, y that is w by h
func CreateFrame(x, y, w, h int, fg, bg termbox.Attribute) *Frame { 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} c := Frame{x: x, y: y, width: w, height: h,
return &s 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 // 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 // SetID sets this control's ID
func (i *Frame) SetID(newID string) { func (c *Frame) SetID(newID string) {
i.id = newID c.id = newID
} }
// GetX returns the x position of the frame // 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 // SetX sets the x position of the frame
func (i *Frame) SetX(x int) { func (c *Frame) SetX(x int) {
i.x = x c.x = x
} }
// GetY returns the y position of the frame // 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 // SetY sets the y position of the frame
func (i *Frame) SetY(y int) { func (c *Frame) SetY(y int) {
i.y = y c.y = y
} }
// GetWidth returns the current width of the frame // 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 // SetWidth sets the current width of the frame
func (i *Frame) SetWidth(w int) { func (c *Frame) SetWidth(w int) {
i.width = w c.width = w
} }
// GetHeight returns the current height of the frame // 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 // SetHeight sets the current height of the frame
func (i *Frame) SetHeight(h int) { func (c *Frame) SetHeight(h int) {
i.height = h c.height = h
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *Frame) SetFgColor(fg termbox.Attribute) { func (c *Frame) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *Frame) SetBgColor(bg termbox.Attribute) { func (c *Frame) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
// IsBordered returns true or false if this frame has a border // 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 // SetBordered sets whether we render a border around the frame
func (i *Frame) SetBordered(b bool) { func (c *Frame) SetBordered(b bool) {
i.bordered = b c.bordered = b
} }
// IsTabSkipped returns whether this modal has it's tabskip flag set // IsTabSkipped returns whether this modal has it's tabskip flag set
func (i *Frame) IsTabSkipped() bool { func (c *Frame) IsTabSkipped() bool {
return i.tabSkip return c.tabSkip
} }
// SetTabSkip sets the tabskip flag for this control // SetTabSkip sets the tabskip flag for this control
func (i *Frame) SetTabSkip(b bool) { func (c *Frame) SetTabSkip(b bool) {
i.tabSkip = b c.tabSkip = b
} }
// AddControl adds a control to the frame // AddControl adds a control to the frame
func (i *Frame) AddControl(t termboxControl) { func (c *Frame) AddControl(t termboxControl) {
i.controls = append(i.controls, t) 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 // GetActiveControl returns the control at tabIdx
func (i *Frame) GetActiveControl() termboxControl { func (c *Frame) GetActiveControl() termboxControl {
if len(i.controls) >= i.tabIdx { if len(c.controls) >= c.tabIdx {
return i.controls[i.tabIdx] if c.controls[c.tabIdx].IsTabSkipped() {
c.FindNextTabStop()
}
return c.controls[c.tabIdx]
} }
return nil return nil
} }
// GetControls returns a slice of all controls // GetControls returns a slice of all controls
func (i *Frame) GetControls() []termboxControl { func (c *Frame) GetControls() []termboxControl {
return i.controls return c.controls
} }
// GetControl returns the control at index i // GetControl returns the control at index i
func (i *Frame) GetControl(idx int) termboxControl { func (c *Frame) GetControl(idx int) termboxControl {
if len(i.controls) >= idx { if len(c.controls) >= idx {
return i.controls[idx] return c.controls[idx]
} }
return nil return nil
} }
// GetControlCount returns the number of controls contained // GetControlCount returns the number of controls contained
func (i *Frame) GetControlCount() int { func (c *Frame) GetControlCount() int {
return len(i.controls) return len(c.controls)
} }
// GetLastControl returns the last control contained // GetLastControl returns the last control contained
func (i *Frame) GetLastControl() termboxControl { func (c *Frame) GetLastControl() termboxControl {
return i.controls[len(i.controls)-1] return c.controls[len(c.controls)-1]
} }
// RemoveAllControls clears the control slice // RemoveAllControls clears the control slice
func (i *Frame) RemoveAllControls() { func (c *Frame) RemoveAllControls() {
i.controls = []termboxControl{} c.controls = []termboxControl{}
} }
// DrawControl figures out the relative position of the control, // DrawControl figures out the relative position of the control,
// sets it, draws it, then resets it. // 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() ctlX, ctlY := t.GetX(), t.GetY()
t.SetX((i.GetX() + ctlX)) t.SetX((c.GetX() + ctlX))
t.SetY((i.GetY() + ctlY)) t.SetY((c.GetY() + ctlY))
t.Draw() t.Draw()
t.SetX(ctlX) t.SetX(ctlX)
t.SetY(ctlY) t.SetY(ctlY)
} }
// GetBottomY returns the y of the lowest control in the frame // GetBottomY returns the y of the lowest control in the frame
func (i *Frame) GetBottomY() int { func (c *Frame) GetBottomY() int {
var ret int var ret int
for idx := range i.controls { for idx := range c.controls {
if i.controls[idx].GetY()+i.controls[idx].GetHeight() > ret { if c.controls[idx].GetY()+c.controls[idx].GetHeight() > ret {
ret = i.controls[idx].GetY() + i.controls[idx].GetHeight() ret = c.controls[idx].GetY() + c.controls[idx].GetHeight()
} }
} }
return ret return ret
} }
// HandleEvent accepts the termbox event and returns whether it was consumed // 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 { if event.Key == termbox.KeyTab {
i.FindNextTabStop() c.FindNextTabStop()
return true 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 // FindNextTabStop finds the next control that can be tabbed to
// A return of true means it found a different one than we started on. // A return of true means it found a different one than we started on.
func (i *Frame) FindNextTabStop() bool { func (c *Frame) FindNextTabStop() bool {
startTab := i.tabIdx startTab := c.tabIdx
i.tabIdx = (i.tabIdx + 1) % len(i.controls) c.tabIdx = (c.tabIdx + 1) % len(c.controls)
for i.controls[i.tabIdx].IsTabSkipped() { for c.controls[c.tabIdx].IsTabSkipped() {
i.tabIdx = (i.tabIdx + 1) % len(i.controls) c.tabIdx = (c.tabIdx + 1) % len(c.controls)
if i.tabIdx == startTab { if c.tabIdx == startTab {
break 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 // Draw outputs the Scoll Frame on the screen
func (i *Frame) Draw() { func (c *Frame) Draw() {
maxWidth := i.width maxWidth := c.width
maxHeight := i.height maxHeight := c.height
x, y := i.x, i.y x, y := c.x, c.y
startX := i.x startX := c.x
startY := i.y startY := c.y
if i.bordered { borderFg, borderBg := c.fg, c.bg
FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) if c.active {
DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) 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-- maxWidth--
maxHeight-- maxHeight--
x++ x++
@ -197,7 +262,12 @@ func (i *Frame) Draw() {
startX++ startX++
startY++ startY++
} }
for idx := range i.controls { for idx := range c.controls {
i.DrawControl(i.controls[idx]) 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 // InputField is a field for inputting text
type InputField struct { type InputField struct {
id string id string
title string
value string value string
x, y, width, height int x, y, width, height int
cursor int cursor int
fg, bg termbox.Attribute fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
cursorFg, cursorBg termbox.Attribute cursorFg, cursorBg termbox.Attribute
bordered bool bordered bool
wrap bool wrap bool
multiline bool multiline bool
tabSkip bool tabSkip bool
active bool
} }
// CreateInputField creates an input field at x, y that is w by h // 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 { 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} c := InputField{x: x, y: y, width: w, height: h,
return &i 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 // 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 // SetID sets this control's ID
func (i *InputField) SetID(newID string) { func (c *InputField) SetID(newID string) {
i.id = newID c.id = newID
} }
// GetValue gets the current text that is in the InputField // 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 // SetValue sets the current text in the InputField to s
func (i *InputField) SetValue(s string) { func (c *InputField) SetValue(s string) {
i.value = s c.value = s
} }
// GetX returns the x position of the input field // 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 // SetX sets the x position of the input field
func (i *InputField) SetX(x int) { func (c *InputField) SetX(x int) {
i.x = x c.x = x
} }
// GetY returns the y position of the input field // 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 // SetY sets the y position of the input field
func (i *InputField) SetY(y int) { func (c *InputField) SetY(y int) {
i.y = y c.y = y
} }
// GetWidth returns the current width of the input field // 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 // SetWidth sets the current width of the input field
func (i *InputField) SetWidth(w int) { func (c *InputField) SetWidth(w int) {
i.width = w c.width = w
} }
// GetHeight returns the current height of the input field // 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 // SetHeight sets the current height of the input field
func (i *InputField) SetHeight(h int) { func (c *InputField) SetHeight(h int) {
i.height = h c.height = h
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *InputField) SetFgColor(fg termbox.Attribute) { func (c *InputField) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *InputField) SetBgColor(bg termbox.Attribute) { func (c *InputField) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
func (i *InputField) SetCursorFg(fg termbox.Attribute) { func (c *InputField) SetCursorFg(fg termbox.Attribute) {
i.cursorFg = fg 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) { func (c *InputField) SetCursorBg(bg termbox.Attribute) {
i.cursorBg = bg 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 // 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 // SetBordered sets whether we render a border around the input field
func (i *InputField) SetBordered(b bool) { func (c *InputField) SetBordered(b bool) {
i.bordered = b c.bordered = b
} }
// IsTabSkipped returns whether this modal has it's tabskip flag set // IsTabSkipped returns whether this modal has it's tabskip flag set
func (i *InputField) IsTabSkipped() bool { func (c *InputField) IsTabSkipped() bool {
return i.tabSkip return c.tabSkip
} }
// SetTabSkip sets the tabskip flag for this control // SetTabSkip sets the tabskip flag for this control
func (i *InputField) SetTabSkip(b bool) { func (c *InputField) SetTabSkip(b bool) {
i.tabSkip = b c.tabSkip = b
} }
// DoesWrap returns true or false if this input field wraps text // 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. // SetWrap sets whether we wrap the text at width.
func (i *InputField) SetWrap(b bool) { func (c *InputField) SetWrap(b bool) {
i.wrap = b c.wrap = b
} }
// IsMultiline returns true or false if this field can have multiple lines // 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 // SetMultiline sets whether the field can have multiple lines
func (i *InputField) SetMultiline(b bool) { func (c *InputField) SetMultiline(b bool) {
i.multiline = b c.multiline = b
} }
// HandleEvent accepts the termbox event and returns whether it was consumed // 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 event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
if i.cursor+len(i.value) > 0 { if c.cursor+len(c.value) > 0 {
crs := len(i.value) crs := len(c.value)
if i.cursor < 0 { if c.cursor < 0 {
crs = i.cursor + len(i.value) crs = c.cursor + len(c.value)
} }
i.value = i.value[:crs-1] + i.value[crs:] c.value = c.value[:crs-1] + c.value[crs:]
//i.value = i.value[:len(i.value)-1] //c.value = c.value[:len(c.value)-1]
} }
} else if event.Key == termbox.KeyArrowLeft { } else if event.Key == termbox.KeyArrowLeft {
if i.cursor+len(i.value) > 0 { if c.cursor+len(c.value) > 0 {
i.cursor-- c.cursor--
} }
} else if event.Key == termbox.KeyArrowRight { } else if event.Key == termbox.KeyArrowRight {
if i.cursor < 0 { if c.cursor < 0 {
i.cursor++ c.cursor++
} }
} else if event.Key == termbox.KeyCtrlU { } else if event.Key == termbox.KeyCtrlU {
// Ctrl+U Clears the Input (before the cursor) // 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 { } else {
// Get the rune to add to our value. Space and Tab are special cases where // Get the rune to add to our value. Space and Tab are special cases where
// we can't use the event's rune directly // we can't use the event's rune directly
@ -162,7 +173,7 @@ func (i *InputField) HandleEvent(event termbox.Event) bool {
case termbox.KeyTab: case termbox.KeyTab:
ch = "\t" ch = "\t"
case termbox.KeyEnter: case termbox.KeyEnter:
if i.multiline { if c.multiline {
ch = "\n" ch = "\n"
} }
default: default:
@ -172,28 +183,32 @@ func (i *InputField) HandleEvent(event termbox.Event) bool {
} }
// TODO: Handle newlines // TODO: Handle newlines
if i.cursor+len(i.value) == 0 { if c.cursor+len(c.value) == 0 {
i.value = string(ch) + i.value c.value = string(ch) + c.value
} else if i.cursor == 0 { } else if c.cursor == 0 {
i.value = i.value + string(ch) c.value = c.value + string(ch)
} else { } else {
strPt1 := i.value[:(len(i.value) + i.cursor)] strPt1 := c.value[:(len(c.value) + c.cursor)]
strPt2 := i.value[(len(i.value) + i.cursor):] strPt2 := c.value[(len(c.value) + c.cursor):]
i.value = strPt1 + string(ch) + strPt2 c.value = strPt1 + string(ch) + strPt2
} }
} }
return true return true
} }
// Draw outputs the input field on the screen // Draw outputs the input field on the screen
func (i *InputField) Draw() { func (c *InputField) Draw() {
maxWidth := i.width maxWidth := c.width
maxHeight := i.height maxHeight := c.height
x, y := i.x, i.y x, y := c.x, c.y
startX := i.x startX := c.x
startY := i.y startY := c.y
if i.bordered { useFg, useBg := c.fg, c.bg
DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.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-- maxWidth--
maxHeight-- maxHeight--
x++ x++
@ -204,53 +219,60 @@ func (i *InputField) Draw() {
var strPt1, strPt2 string var strPt1, strPt2 string
var cursorRune rune var cursorRune rune
if len(i.value) > 0 { if len(c.value) > 0 {
if i.cursor+len(i.value) == 0 { if c.cursor+len(c.value) == 0 {
strPt1 = "" strPt1 = ""
strPt2 = i.value[1:] strPt2 = c.value[1:]
cursorRune = rune(i.value[0]) cursorRune = rune(c.value[0])
} else if i.cursor == 0 { } else if c.cursor == 0 {
strPt1 = i.value strPt1 = c.value
strPt2 = "" strPt2 = ""
cursorRune = ' ' cursorRune = ' '
} else { } else {
strPt1 = i.value[:(len(i.value) + i.cursor)] strPt1 = c.value[:(len(c.value) + c.cursor)]
strPt2 = i.value[(len(i.value)+i.cursor)+1:] strPt2 = c.value[(len(c.value)+c.cursor)+1:]
cursorRune = rune(i.value[len(i.value)+i.cursor]) cursorRune = rune(c.value[len(c.value)+c.cursor])
} }
} else { } else {
strPt1, strPt2, cursorRune = "", "", ' ' 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 // Split the text into maxWidth chunks
for len(strPt1) > maxWidth { for len(strPt1) > maxWidth {
breakAt := maxWidth breakAt := maxWidth
DrawStringAtPoint(strPt1[:breakAt], x, y, i.fg, i.bg) DrawStringAtPoint(strPt1[:breakAt], x, y, useFg, useBg)
x = startX x = startX
y++ y++
strPt1 = strPt1[breakAt:] strPt1 = strPt1[breakAt:]
} }
x, y = DrawStringAtPoint(strPt1, x, y, i.fg, i.bg) x, y = DrawStringAtPoint(strPt1, x, y, useFg, useBg)
if x >= maxWidth { if x >= maxWidth {
y++ y++
x = startX x = startX
} }
termbox.SetCell(x, y, cursorRune, i.cursorFg, i.cursorBg) termbox.SetCell(x, y, cursorRune, c.cursorFg, c.cursorBg)
x++ x++
if len(strPt2) > 0 { if len(strPt2) > 0 {
lenLeft := maxWidth - len(strPt1) - 1 lenLeft := maxWidth - len(strPt1) - 1
if lenLeft > 0 && len(strPt2) > lenLeft { 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:] strPt2 = strPt2[lenLeft:]
} }
for len(strPt2) > maxWidth { for len(strPt2) > maxWidth {
breakAt := maxWidth breakAt := maxWidth
DrawStringAtPoint(strPt2[:breakAt], x, y, i.fg, i.bg) DrawStringAtPoint(strPt2[:breakAt], x, y, useFg, useBg)
x = startX x = startX
y++ y++
strPt2 = strPt2[breakAt:] strPt2 = strPt2[breakAt:]
} }
x, y = DrawStringAtPoint(strPt2, x, y, i.fg, i.bg) x, y = DrawStringAtPoint(strPt2, x, y, useFg, useBg)
} }
} else { } else {
for len(strPt1)+len(strPt2)+1 > maxWidth { for len(strPt1)+len(strPt2)+1 > maxWidth {
@ -263,8 +285,12 @@ func (i *InputField) Draw() {
strPt2 = strPt2[:len(strPt2)-1] strPt2 = strPt2[:len(strPt2)-1]
} }
} }
x, y = DrawStringAtPoint(strPt1, i.x+1, i.y+1, i.fg, i.bg) x, y = DrawStringAtPoint(strPt1, c.x+len(c.title), c.y, useFg, useBg)
termbox.SetCell(x, y, cursorRune, i.cursorFg, i.cursorBg) if c.active {
DrawStringAtPoint(strPt2, x+1, y, i.fg, i.bg) 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 showHelp bool
cursor int cursor int
bg, fg termbox.Attribute bg, fg termbox.Attribute
activeFg, activeBg termbox.Attribute
isDone bool isDone bool
isAccepted bool isAccepted bool
isVisible bool isVisible bool
bordered bool bordered bool
tabSkip bool tabSkip bool
inputSelected bool inputSelected bool
active bool
} }
// CreateInputModal Create an input modal with the given attributes // CreateInputModal Create an input modal with the given attributes
func CreateInputModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *InputModal { 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} c := 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) c.input = CreateInputField(c.x+2, c.y+3, c.width-2, 2, c.fg, c.bg)
i.showHelp = true c.showHelp = true
i.input.bordered = true c.input.bordered = true
i.isVisible = true c.isVisible = true
i.inputSelected = true c.inputSelected = true
return &i 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 // 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 // SetID sets this control's ID
func (i *InputModal) SetID(newID string) { func (c *InputModal) SetID(newID string) {
i.id = newID c.id = newID
} }
// GetTitle Return the title of the modal // 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 // SetTitle Sets the title of the modal to s
func (i *InputModal) SetTitle(s string) { func (c *InputModal) SetTitle(s string) {
i.title = s c.title = s
} }
// GetText Return the text of the modal // 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 // SetText Set the text of the modal to s
func (i *InputModal) SetText(s string) { func (c *InputModal) SetText(s string) {
i.text = s c.text = s
} }
// GetX Return the x position of the modal // 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 // SetX set the x position of the modal to x
func (i *InputModal) SetX(x int) { func (c *InputModal) SetX(x int) {
i.x = x c.x = x
} }
// GetY Return the y position of the modal // 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 // SetY Set the y position of the modal to y
func (i *InputModal) SetY(y int) { func (c *InputModal) SetY(y int) {
i.y = y c.y = y
} }
// GetWidth Return the width of the modal // 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 // SetWidth Set the width of the modal to width
func (i *InputModal) SetWidth(width int) { func (c *InputModal) SetWidth(width int) {
i.width = width c.width = width
} }
// GetHeight Return the height of the modal // 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 // SetHeight Set the height of the modal to height
func (i *InputModal) SetHeight(height int) { func (c *InputModal) SetHeight(height int) {
i.height = height c.height = height
} }
// SetMultiline returns whether this is a multiline modal // SetMultiline returns whether this is a multiline modal
func (i *InputModal) SetMultiline(m bool) { func (c *InputModal) SetMultiline(m bool) {
i.input.multiline = m c.input.multiline = m
} }
// IsMultiline returns whether this is a multiline modal // IsMultiline returns whether this is a multiline modal
func (i *InputModal) IsMultiline() bool { func (c *InputModal) IsMultiline() bool {
return i.input.multiline return c.input.multiline
} }
// IsBordered returns whether this control is bordered or not // IsBordered returns whether this control is bordered or not
func (i *InputModal) IsBordered() bool { func (c *InputModal) IsBordered() bool {
return i.bordered return c.bordered
} }
// SetBordered sets whether we render a border around the frame // SetBordered sets whether we render a border around the frame
func (i *InputModal) SetBordered(b bool) { func (c *InputModal) SetBordered(b bool) {
i.bordered = b c.bordered = b
} }
// IsTabSkipped returns whether this control has it's tabskip flag set // IsTabSkipped returns whether this control has it's tabskip flag set
func (i *InputModal) IsTabSkipped() bool { func (c *InputModal) IsTabSkipped() bool {
return i.tabSkip return c.tabSkip
} }
// SetTabSkip sets the tabskip flag for this control // SetTabSkip sets the tabskip flag for this control
func (i *InputModal) SetTabSkip(b bool) { func (c *InputModal) SetTabSkip(b bool) {
i.tabSkip = b c.tabSkip = b
} }
// HelpIsShown Returns whether the modal is showing it's help text or not // 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 // ShowHelp Set the "Show Help" flag
func (i *InputModal) ShowHelp(b bool) { func (c *InputModal) ShowHelp(b bool) {
i.showHelp = b c.showHelp = b
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *InputModal) SetFgColor(fg termbox.Attribute) { func (c *InputModal) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *InputModal) SetBgColor(bg termbox.Attribute) { func (c *InputModal) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
// Show Sets the visibility flag to true // Show Sets the visibility flag to true
func (i *InputModal) Show() { func (c *InputModal) Show() {
i.isVisible = true c.isVisible = true
} }
// Hide Sets the visibility flag to false // Hide Sets the visibility flag to false
func (i *InputModal) Hide() { func (c *InputModal) Hide() {
i.isVisible = false c.isVisible = false
} }
// IsVisible returns the isVisible flag // IsVisible returns the isVisible flag
func (i *InputModal) IsVisible() bool { func (c *InputModal) IsVisible() bool {
return i.isVisible return c.isVisible
} }
// SetDone Sets the flag that tells whether this modal has completed it's purpose // SetDone Sets the flag that tells whether this modal has completed it's purpose
func (i *InputModal) SetDone(b bool) { func (c *InputModal) SetDone(b bool) {
i.isDone = b c.isDone = b
} }
// IsDone Returns the "isDone" flag // IsDone Returns the "isDone" flag
func (i *InputModal) IsDone() bool { func (c *InputModal) IsDone() bool {
return i.isDone return c.isDone
} }
// IsAccepted Returns whether the modal has been accepted // IsAccepted Returns whether the modal has been accepted
func (i *InputModal) IsAccepted() bool { func (c *InputModal) IsAccepted() bool {
return i.isAccepted return c.isAccepted
} }
// GetValue Return the current value of the input // 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 // SetValue Sets the value of the input to s
func (i *InputModal) SetValue(s string) { func (c *InputModal) SetValue(s string) {
i.input.SetValue(s) c.input.SetValue(s)
} }
// SetInputWrap sets whether the input field will wrap long text or not // SetInputWrap sets whether the input field will wrap long text or not
func (i *InputModal) SetInputWrap(b bool) { func (c *InputModal) SetInputWrap(b bool) {
i.input.SetWrap(b) c.input.SetWrap(b)
} }
// Clear Resets all non-positional parameters of the modal // Clear Resets all non-positional parameters of the modal
func (i *InputModal) Clear() { func (c *InputModal) Clear() {
i.title = "" c.title = ""
i.text = "" c.text = ""
i.input.SetValue("") c.input.SetValue("")
i.isDone = false c.isDone = false
i.isVisible = false c.isVisible = false
} }
// HandleEvent Handle the termbox event, return true if it was consumed // 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 event.Key == termbox.KeyEnter {
if !i.input.IsMultiline() || !i.inputSelected { if !c.input.IsMultiline() || !c.inputSelected {
// Done editing // Done editing
i.isDone = true c.isDone = true
i.isAccepted = true c.isAccepted = true
} else { } else {
i.input.HandleEvent(event) c.input.HandleEvent(event)
} }
return true return true
} else if event.Key == termbox.KeyTab { } else if event.Key == termbox.KeyTab {
if i.input.IsMultiline() { if c.input.IsMultiline() {
i.inputSelected = !i.inputSelected c.inputSelected = !c.inputSelected
} }
} else if event.Key == termbox.KeyEsc { } else if event.Key == termbox.KeyEsc {
// Done editing // Done editing
i.isDone = true c.isDone = true
i.isAccepted = false c.isAccepted = false
return true return true
} }
return i.input.HandleEvent(event) return c.input.HandleEvent(event)
} }
// Draw Draw the modal // Draw Draw the modal
func (i *InputModal) Draw() { func (c *InputModal) Draw() {
if i.isVisible { if c.isVisible {
// First blank out the area we'll be putting the modal // 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) FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg)
nextY := i.y + 1 nextY := c.y + 1
// The title // The title
if i.title != "" { if c.title != "" {
if len(i.title) > i.width { if len(c.title) > c.width {
diff := i.width - len(i.title) diff := c.width - len(c.title)
DrawStringAtPoint(i.title[:len(i.title)+diff-1], i.x+1, nextY, i.fg, i.bg) DrawStringAtPoint(c.title[:len(c.title)+diff-1], c.x+1, nextY, c.fg, c.bg)
} else { } else {
DrawStringAtPoint(i.title, i.x+1, nextY, i.fg, i.bg) DrawStringAtPoint(c.title, c.x+1, nextY, c.fg, c.bg)
} }
nextY++ 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++ nextY++
} }
if i.text != "" { if c.text != "" {
DrawStringAtPoint(i.text, i.x+1, nextY, i.fg, i.bg) DrawStringAtPoint(c.text, c.x+1, nextY, c.fg, c.bg)
nextY++ nextY++
} }
i.input.SetY(nextY) c.input.SetY(nextY)
i.input.Draw() c.input.Draw()
nextY += 3 nextY += 3
if i.showHelp { if c.showHelp {
helpString := " (ENTER) to Accept. (ESC) to Cancel. " helpString := " (ENTER) to Accept. (ESC) to Cancel. "
helpX := (i.x + i.width - len(helpString)) - 1 helpX := (c.x + c.width - len(helpString)) - 1
DrawStringAtPoint(helpString, helpX, nextY, i.fg, i.bg) DrawStringAtPoint(helpString, helpX, nextY, c.fg, c.bg)
} }
if i.bordered { if c.bordered {
// Now draw the border // 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 x, y, width, height int
cursor int cursor int
fg, bg termbox.Attribute fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool bordered bool
wrap bool wrap bool
multiline bool multiline bool
active bool
} }
// CreateLabel creates an input field at x, y that is w by h // 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 { 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} c := Label{
return &i 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 // IsTabSkipped is always true for a label
func (i *Label) IsTabSkipped() bool { func (c *Label) IsTabSkipped() bool { return true }
return true
}
// This doesn't do anything for a label // 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 // 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 // SetID sets this control's ID
func (i *Label) SetID(newID string) { func (c *Label) SetID(newID string) { c.id = newID }
i.id = newID
}
// GetValue gets the current text that is in the Label // 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 // SetValue sets the current text in the Label to s
func (i *Label) SetValue(s string) { func (c *Label) SetValue(s string) { c.value = s }
i.value = s
}
// GetX returns the x position of the input field // 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 // SetX sets the x position of the input field
func (i *Label) SetX(x int) { func (c *Label) SetX(x int) { c.x = x }
i.x = x
}
// GetY returns the y position of the input field // 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 // SetY sets the y position of the input field
func (i *Label) SetY(y int) { func (c *Label) SetY(y int) { c.y = y }
i.y = y
}
// GetWidth returns the current width of the input field // GetWidth returns the current width of the input field
func (i *Label) GetWidth() int { func (c *Label) GetWidth() int {
if i.width == -1 { if c.width == -1 {
if i.bordered { if c.bordered {
return len(i.value) + 2 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 // SetWidth sets the current width of the input field
func (i *Label) SetWidth(w int) { func (c *Label) SetWidth(w int) {
i.width = w c.width = w
} }
// GetHeight returns the current height of the input field // 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 // SetHeight sets the current height of the input field
func (i *Label) SetHeight(h int) { func (c *Label) SetHeight(h int) {
i.height = h c.height = h
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *Label) SetFgColor(fg termbox.Attribute) { func (c *Label) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *Label) SetBgColor(bg termbox.Attribute) { func (c *Label) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
// IsBordered returns true or false if this input field has a border // 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 // SetBordered sets whether we render a border around the input field
func (i *Label) SetBordered(b bool) { func (c *Label) SetBordered(b bool) {
i.bordered = b c.bordered = b
} }
// DoesWrap returns true or false if this input field wraps text // 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. // SetWrap sets whether we wrap the text at width.
func (i *Label) SetWrap(b bool) { func (c *Label) SetWrap(b bool) {
i.wrap = b c.wrap = b
} }
// IsMultiline returns true or false if this field can have multiple lines // 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 // SetMultiline sets whether the field can have multiple lines
func (i *Label) SetMultiline(b bool) { func (c *Label) SetMultiline(b bool) {
i.multiline = b c.multiline = b
} }
// HandleEvent accepts the termbox event and returns whether it was consumed // 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 // Draw outputs the input field on the screen
func (i *Label) Draw() { func (c *Label) Draw() {
maxWidth := i.width maxWidth := c.width
maxHeight := i.height maxHeight := c.height
x, y := i.x, i.y x, y := c.x, c.y
startX := i.x startX := c.x
startY := i.y startY := c.y
if i.bordered { if c.bordered {
DrawBorder(i.x, i.y, i.x+i.GetWidth(), i.y+i.height, i.fg, i.bg) DrawBorder(c.x, c.y, c.x+c.GetWidth(), c.y+c.height, c.fg, c.bg)
maxWidth-- maxWidth--
maxHeight-- maxHeight--
x++ x++
@ -144,5 +144,5 @@ func (i *Label) Draw() {
startY++ 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 bg, fg termbox.Attribute
selectedBg, selectedFg termbox.Attribute selectedBg, selectedFg termbox.Attribute
disabledBg, disabledFg termbox.Attribute disabledBg, disabledFg termbox.Attribute
activeFg, activeBg termbox.Attribute
isDone bool isDone bool
bordered bool bordered bool
vimMode bool vimMode bool
tabSkip bool tabSkip bool
active bool
} }
// CreateMenu Creates a menu with the specified attributes // CreateMenu Creates a menu with the specified attributes
func CreateMenu(title string, options []string, x, y, width, height int, fg, bg termbox.Attribute) *Menu { func CreateMenu(title string, options []string, x, y, width, height int, fg, bg termbox.Attribute) *Menu {
i := Menu{ c := Menu{
title: title, title: title,
x: x, y: y, width: width, height: height, x: x, y: y, width: width, height: height,
fg: fg, bg: bg, selectedFg: bg, selectedBg: fg, fg: fg, bg: bg, selectedFg: bg, selectedBg: fg,
disabledFg: bg, disabledBg: bg, disabledFg: bg, disabledBg: bg,
activeFg: fg, activeBg: bg,
bordered: true,
} }
for _, line := range options { 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 { if len(c.options) > 0 {
i.SetSelectedOption(&i.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 // 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 // SetID sets this control's ID
func (i *Menu) SetID(newID string) { func (c *Menu) SetID(newID string) { c.id = newID }
i.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 // 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 // SetTitle sets the current title of the menu to s
func (i *Menu) SetTitle(s string) { func (c *Menu) SetTitle(s string) {
i.title = s c.title = s
} }
// GetOptions returns the current options of the menu // GetOptions returns the current options of the menu
func (i *Menu) GetOptions() []MenuOption { func (c *Menu) GetOptions() []MenuOption {
return i.options return c.options
} }
// SetOptions set the menu's options to opts // SetOptions set the menu's options to opts
func (i *Menu) SetOptions(opts []MenuOption) { func (c *Menu) SetOptions(opts []MenuOption) {
i.options = opts c.options = opts
} }
// SetOptionsFromStrings sets the options of this menu from a slice of strings // 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 var newOpts []MenuOption
for _, v := range opts { for _, v := range opts {
newOpts = append(newOpts, *CreateOptionFromText(v)) newOpts = append(newOpts, *CreateOptionFromText(v))
} }
i.SetOptions(newOpts) c.SetOptions(newOpts)
i.SetSelectedOption(i.GetOptionFromIndex(0)) c.SetSelectedOption(c.GetOptionFromIndex(0))
} }
// GetX returns the current x coordinate of the menu // 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 // SetX sets the current x coordinate of the menu to x
func (i *Menu) SetX(x int) { func (c *Menu) SetX(x int) {
i.x = x c.x = x
} }
// GetY returns the current y coordinate of the menu // 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 // SetY sets the current y coordinate of the menu to y
func (i *Menu) SetY(y int) { func (c *Menu) SetY(y int) {
i.y = y c.y = y
} }
// GetWidth returns the current width of the menu // 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 // SetWidth sets the current menu width to width
func (i *Menu) SetWidth(width int) { func (c *Menu) SetWidth(width int) {
i.width = width c.width = width
} }
// GetHeight returns the current height of the menu // 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 // SetHeight set the height of the menu to height
func (i *Menu) SetHeight(height int) { func (c *Menu) SetHeight(height int) {
i.height = height c.height = height
} }
// GetSelectedOption returns the current selected option // GetSelectedOption returns the current selected option
func (i *Menu) GetSelectedOption() *MenuOption { func (c *Menu) GetSelectedOption() *MenuOption {
idx := i.GetSelectedIndex() idx := c.GetSelectedIndex()
if idx != -1 { if idx != -1 {
return &i.options[idx] return &c.options[idx]
} }
return nil return nil
} }
// GetOptionFromIndex Returns the // GetOptionFromIndex Returns the
func (i *Menu) GetOptionFromIndex(idx int) *MenuOption { func (c *Menu) GetOptionFromIndex(idx int) *MenuOption {
if idx >= 0 && idx < len(i.options) { if idx >= 0 && idx < len(c.options) {
return &i.options[idx] return &c.options[idx]
} }
return nil return nil
} }
// GetOptionFromText Returns the first option with the text v // GetOptionFromText Returns the first option with the text v
func (i *Menu) GetOptionFromText(v string) *MenuOption { func (c *Menu) GetOptionFromText(v string) *MenuOption {
for idx := range i.options { for idx := range c.options {
testOption := &i.options[idx] testOption := &c.options[idx]
if testOption.GetText() == v { if testOption.GetText() == v {
return testOption return testOption
} }
@ -135,9 +151,9 @@ func (i *Menu) GetOptionFromText(v string) *MenuOption {
// GetSelectedIndex returns the index of the selected option // GetSelectedIndex returns the index of the selected option
// Returns -1 if nothing is selected // Returns -1 if nothing is selected
func (i *Menu) GetSelectedIndex() int { func (c *Menu) GetSelectedIndex() int {
for idx := range i.options { for idx := range c.options {
if i.options[idx].IsSelected() { if c.options[idx].IsSelected() {
return idx return idx
} }
} }
@ -145,267 +161,288 @@ func (i *Menu) GetSelectedIndex() int {
} }
// SetSelectedIndex sets the selection to setIdx // SetSelectedIndex sets the selection to setIdx
func (i *Menu) SetSelectedIndex(idx int) { func (c *Menu) SetSelectedIndex(idx int) {
if len(i.options) > 0 { if len(c.options) > 0 {
if idx < 0 { if idx < 0 {
idx = 0 idx = 0
} else if idx >= len(i.options) { } else if idx >= len(c.options) {
idx = len(i.options) - 1 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) // SetSelectedOption sets the current selected option to v (if it's valid)
func (i *Menu) SetSelectedOption(v *MenuOption) { func (c *Menu) SetSelectedOption(v *MenuOption) {
for idx := range i.options { for idx := range c.options {
if &i.options[idx] == v { if &c.options[idx] == v {
i.options[idx].Select() c.options[idx].Select()
} else { } else {
i.options[idx].Unselect() c.options[idx].Unselect()
} }
} }
} }
// SelectPrevOption Decrements the selected option (if it can) // SelectPrevOption Decrements the selected option (if it can)
func (i *Menu) SelectPrevOption() { func (c *Menu) SelectPrevOption() {
idx := i.GetSelectedIndex() idx := c.GetSelectedIndex()
for idx >= 0 { for idx >= 0 {
idx-- idx--
testOption := i.GetOptionFromIndex(idx) testOption := c.GetOptionFromIndex(idx)
if testOption != nil && !testOption.IsDisabled() { if testOption != nil && !testOption.IsDisabled() {
i.SetSelectedOption(testOption) c.SetSelectedOption(testOption)
return return
} }
} }
} }
// SelectNextOption Increments the selected option (if it can) // SelectNextOption Increments the selected option (if it can)
func (i *Menu) SelectNextOption() { func (c *Menu) SelectNextOption() {
idx := i.GetSelectedIndex() idx := c.GetSelectedIndex()
for idx < len(i.options) { for idx < len(c.options) {
idx++ idx++
testOption := i.GetOptionFromIndex(idx) testOption := c.GetOptionFromIndex(idx)
if testOption != nil && !testOption.IsDisabled() { if testOption != nil && !testOption.IsDisabled() {
i.SetSelectedOption(testOption) c.SetSelectedOption(testOption)
return return
} }
} }
} }
// SelectPageUpOption Goes up 'menu height' options // SelectPageUpOption Goes up 'menu height' options
func (i *Menu) SelectPageUpOption() { func (c *Menu) SelectPageUpOption() {
idx := i.GetSelectedIndex() idx := c.GetSelectedIndex()
idx -= i.height idx -= c.height
if idx < 0 { if idx < 0 {
idx = 0 idx = 0
} }
i.SetSelectedIndex(idx) c.SetSelectedIndex(idx)
return return
} }
// SelectPageDownOption Goes down 'menu height' options // SelectPageDownOption Goes down 'menu height' options
func (i *Menu) SelectPageDownOption() { func (c *Menu) SelectPageDownOption() {
idx := i.GetSelectedIndex() idx := c.GetSelectedIndex()
idx += i.height idx += c.height
if idx >= len(i.options) { if idx >= len(c.options) {
idx = len(i.options) - 1 idx = len(c.options) - 1
} }
i.SetSelectedIndex(idx) c.SetSelectedIndex(idx)
return return
} }
// SelectFirstOption Goes to the top // SelectFirstOption Goes to the top
func (i *Menu) SelectFirstOption() { func (c *Menu) SelectFirstOption() {
i.SetSelectedIndex(0) c.SetSelectedIndex(0)
return return
} }
// SelectLastOption Goes to the bottom // SelectLastOption Goes to the bottom
func (i *Menu) SelectLastOption() { func (c *Menu) SelectLastOption() {
i.SetSelectedIndex(len(i.options) - 1) c.SetSelectedIndex(len(c.options) - 1)
return return
} }
// SetOptionDisabled Disables the specified option // SetOptionDisabled Disables the specified option
func (i *Menu) SetOptionDisabled(idx int) { func (c *Menu) SetOptionDisabled(idx int) {
if len(i.options) > idx { if len(c.options) > idx {
i.GetOptionFromIndex(idx).Disable() c.GetOptionFromIndex(idx).Disable()
} }
} }
// SetOptionEnabled Enables the specified option // SetOptionEnabled Enables the specified option
func (i *Menu) SetOptionEnabled(idx int) { func (c *Menu) SetOptionEnabled(idx int) {
if len(i.options) > idx { if len(c.options) > idx {
i.GetOptionFromIndex(idx).Enable() c.GetOptionFromIndex(idx).Enable()
} }
} }
// HelpIsShown returns true or false if the help is displayed // 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 // ShowHelp sets whether or not to display the help text
func (i *Menu) ShowHelp(b bool) { func (c *Menu) ShowHelp(b bool) {
i.showHelp = b c.showHelp = b
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *Menu) SetFgColor(fg termbox.Attribute) { func (c *Menu) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *Menu) SetBgColor(bg termbox.Attribute) { func (c *Menu) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
// IsDone returns whether the user has answered the modal // 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 // SetDone sets whether the modal has completed it's purpose
func (i *Menu) SetDone(b bool) { func (c *Menu) SetDone(b bool) {
i.isDone = b c.isDone = b
} }
// IsBordered returns true or false if this menu has a border // 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 // SetBordered sets whether we render a border around the menu
func (i *Menu) SetBordered(b bool) { func (c *Menu) SetBordered(b bool) {
i.bordered = b c.bordered = b
} }
// EnableVimMode Enables h,j,k,l navigation // EnableVimMode Enables h,j,k,l navigation
func (i *Menu) EnableVimMode() { func (c *Menu) EnableVimMode() {
i.vimMode = true c.vimMode = true
} }
// DisableVimMode Disables h,j,k,l navigation // DisableVimMode Disables h,j,k,l navigation
func (i *Menu) DisableVimMode() { func (c *Menu) DisableVimMode() {
i.vimMode = false c.vimMode = false
} }
// HandleEvent handles the termbox event and returns whether it was consumed // 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 { if event.Key == termbox.KeyEnter || event.Key == termbox.KeySpace {
i.isDone = true c.isDone = true
return true return true
} }
currentIdx := i.GetSelectedIndex() currentIdx := c.GetSelectedIndex()
switch event.Key { switch event.Key {
case termbox.KeyArrowUp: case termbox.KeyArrowUp:
i.SelectPrevOption() c.SelectPrevOption()
case termbox.KeyArrowDown: case termbox.KeyArrowDown:
i.SelectNextOption() c.SelectNextOption()
case termbox.KeyArrowLeft: case termbox.KeyArrowLeft:
i.SelectPageUpOption() c.SelectPageUpOption()
case termbox.KeyArrowRight: case termbox.KeyArrowRight:
i.SelectPageDownOption() c.SelectPageDownOption()
} }
if i.vimMode { if c.vimMode {
switch event.Ch { switch event.Ch {
case 'j': case 'j':
i.SelectNextOption() c.SelectNextOption()
case 'k': case 'k':
i.SelectPrevOption() c.SelectPrevOption()
} }
if event.Key == termbox.KeyCtrlF { if event.Key == termbox.KeyCtrlF {
i.SelectPageDownOption() c.SelectPageDownOption()
} else if event.Key == termbox.KeyCtrlB { } else if event.Key == termbox.KeyCtrlB {
i.SelectPageUpOption() c.SelectPageUpOption()
} }
} }
if i.GetSelectedIndex() != currentIdx { if c.GetSelectedIndex() != currentIdx {
return true return true
} }
return false return false
} }
// Draw draws the modal // Draw draws the modal
func (i *Menu) Draw() { func (c *Menu) Draw() {
// First blank out the area we'll be putting the menu useFg, useBg := c.fg, c.bg
FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) if c.active {
// Now draw the border useFg, useBg = c.activeFg, c.activeBg
optionStartX := i.x
optionStartY := i.y
optionWidth := i.width
optionHeight := i.height
if optionHeight == -1 {
optionHeight = len(i.options)
} }
if i.bordered { // First blank out the area we'll be putting the menu
if i.height == -1 { FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, useFg, useBg)
DrawBorder(i.x, i.y, i.x+i.width, i.y+optionHeight+1, i.fg, i.bg) // 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 { } 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 optionStartX = c.x + 1
optionStartY = i.y + 1 optionStartY = c.y + 1
optionWidth = i.width - 1 optionWidth = c.width - 1
optionHeight -= 2 optionHeight -= 2
} }
// The title if len(c.options) > 0 {
if i.title != "" { firstDispIdx := 0
DrawStringAtPoint(AlignText(i.title, optionWidth, AlignCenter), optionStartX, optionStartY, i.fg, i.bg) lastDispIdx := len(c.options) - 1
optionStartY++ if len(c.options) > c.height-2 {
if i.bordered { lastDispIdx = c.height - 2
FillWithChar('-', optionStartX, optionStartY, optionWidth, optionStartY, i.fg, i.bg)
optionStartY++
optionHeight--
} }
optionHeight-- if c.GetSelectedIndex() > c.height-2 {
} firstDispIdx = c.GetSelectedIndex() - (c.height - 2)
lastDispIdx = c.GetSelectedIndex()
if len(i.options) > 0 {
// If the currently selected option is disabled, move to the next
if i.GetSelectedOption().IsDisabled() {
i.SelectNextOption()
} }
for idx := firstDispIdx; idx < lastDispIdx; idx++ {
// Print the options currOpt := &c.options[idx]
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]
outTxt := currOpt.GetText() outTxt := currOpt.GetText()
if len(outTxt) >= i.width {
outTxt = outTxt[:i.width]
}
if currOpt.IsDisabled() { if currOpt.IsDisabled() {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.disabledFg, i.disabledBg) DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.disabledFg, c.disabledBg)
} else if i.GetSelectedOption() == currOpt { } else if c.GetSelectedOption() == currOpt {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.selectedFg, i.selectedBg) DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.selectedFg, c.selectedBg)
} else { } else {
DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.fg, i.bg) DrawStringAtPoint(outTxt, optionStartX, optionStartY, useFg, useBg)
} }
optionStartY++ 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 */ /* MenuOption Struct & methods */
@ -427,52 +464,52 @@ func CreateOptionFromText(s string) *MenuOption {
} }
// SetText Sets the text for this option // SetText Sets the text for this option
func (i *MenuOption) SetText(s string) { func (c *MenuOption) SetText(s string) {
i.text = s c.text = s
} }
// GetText Returns the text for this option // 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 // Disable Sets this option to disabled
func (i *MenuOption) Disable() { func (c *MenuOption) Disable() {
i.disabled = true c.disabled = true
} }
// Enable Sets this option to enabled // Enable Sets this option to enabled
func (i *MenuOption) Enable() { func (c *MenuOption) Enable() {
i.disabled = false c.disabled = false
} }
// IsDisabled returns whether this option is enabled // IsDisabled returns whether this option is enabled
func (i *MenuOption) IsDisabled() bool { func (c *MenuOption) IsDisabled() bool {
return i.disabled return c.disabled
} }
// IsSelected Returns whether this option is selected // IsSelected Returns whether this option is selected
func (i *MenuOption) IsSelected() bool { func (c *MenuOption) IsSelected() bool {
return i.selected return c.selected
} }
// Select Sets this option to selected // Select Sets this option to selected
func (i *MenuOption) Select() { func (c *MenuOption) Select() {
i.selected = true c.selected = true
} }
// Unselect Sets this option to not selected // Unselect Sets this option to not selected
func (i *MenuOption) Unselect() { func (c *MenuOption) Unselect() {
i.selected = false c.selected = false
} }
// SetHelpText Sets this option's help text to s // SetHelpText Sets this option's help text to s
func (i *MenuOption) SetHelpText(s string) { func (c *MenuOption) SetHelpText(s string) {
i.helpText = s c.helpText = s
} }
// GetHelpText Returns the help text for this option // 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 // AddToSubMenu adds a slice of MenuOptions to this option
func (i *MenuOption) AddToSubMenu(sub *MenuOption) { func (c *MenuOption) AddToSubMenu(sub *MenuOption) {
i.subMenu = append(i.subMenu, *sub) c.subMenu = append(c.subMenu, *sub)
} }

View File

@ -15,157 +15,165 @@ type ProgressBar struct {
alignment TextAlignment alignment TextAlignment
colorized bool colorized bool
x, y int x, y int
width, height int width, height int
bg, fg termbox.Attribute fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
active bool
} }
// CreateProgressBar Create a progress bar object // CreateProgressBar Create a progress bar object
func CreateProgressBar(tot, x, y int, fg, bg termbox.Attribute) *ProgressBar { func CreateProgressBar(tot, x, y int, fg, bg termbox.Attribute) *ProgressBar {
i := ProgressBar{total: tot, c := ProgressBar{total: tot,
fullChar: '#', emptyChar: ' ', fullChar: '#', emptyChar: ' ',
x: x, y: y, height: 1, width: 10, x: x, y: y, height: 1, width: 10,
bordered: true, fg: fg, bg: bg, bordered: true, fg: fg, bg: bg,
activeFg: fg, activeBg: bg,
alignment: AlignLeft, 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 // 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 // SetID sets this control's ID
func (i *ProgressBar) SetID(newID string) { func (c *ProgressBar) SetID(newID string) {
i.id = newID c.id = newID
} }
// GetProgress returns the curret progress value // GetProgress returns the curret progress value
func (i *ProgressBar) GetProgress() int { func (c *ProgressBar) GetProgress() int {
return i.progress return c.progress
} }
// SetProgress sets the current progress of the bar // SetProgress sets the current progress of the bar
func (i *ProgressBar) SetProgress(p int) { func (c *ProgressBar) SetProgress(p int) {
if (p <= i.total || i.allowOverflow) || (p >= 0 || i.allowUnderflow) { if (p <= c.total || c.allowOverflow) || (p >= 0 || c.allowUnderflow) {
i.progress = p c.progress = p
} }
} }
// IncrProgress increments the current progress of the bar // IncrProgress increments the current progress of the bar
func (i *ProgressBar) IncrProgress() { func (c *ProgressBar) IncrProgress() {
if i.progress < i.total || i.allowOverflow { if c.progress < c.total || c.allowOverflow {
i.progress++ c.progress++
} }
} }
// DecrProgress decrements the current progress of the bar // DecrProgress decrements the current progress of the bar
func (i *ProgressBar) DecrProgress() { func (c *ProgressBar) DecrProgress() {
if i.progress > 0 || i.allowUnderflow { if c.progress > 0 || c.allowUnderflow {
i.progress-- c.progress--
} }
} }
// GetPercent returns the percent full of the bar // GetPercent returns the percent full of the bar
func (i *ProgressBar) GetPercent() int { func (c *ProgressBar) GetPercent() int {
return int(float64(i.progress) / float64(i.total) * 100) return int(float64(c.progress) / float64(c.total) * 100)
} }
// EnableOverflow Tells the progress bar that it can go over the total // EnableOverflow Tells the progress bar that it can go over the total
func (i *ProgressBar) EnableOverflow() { func (c *ProgressBar) EnableOverflow() {
i.allowOverflow = true c.allowOverflow = true
} }
// DisableOverflow Tells the progress bar that it can NOT go over the total // DisableOverflow Tells the progress bar that it can NOT go over the total
func (i *ProgressBar) DisableOverflow() { func (c *ProgressBar) DisableOverflow() {
i.allowOverflow = false c.allowOverflow = false
} }
// EnableUnderflow Tells the progress bar that it can go below zero // EnableUnderflow Tells the progress bar that it can go below zero
func (i *ProgressBar) EnableUnderflow() { func (c *ProgressBar) EnableUnderflow() {
i.allowUnderflow = true c.allowUnderflow = true
} }
// DisableUnderflow Tells the progress bar that it can NOT go below zero // DisableUnderflow Tells the progress bar that it can NOT go below zero
func (i *ProgressBar) DisableUnderflow() { func (c *ProgressBar) DisableUnderflow() {
i.allowUnderflow = false c.allowUnderflow = false
} }
// GetFullChar returns the rune used for 'full' // GetFullChar returns the rune used for 'full'
func (i *ProgressBar) GetFullChar() rune { func (c *ProgressBar) GetFullChar() rune {
return i.fullChar return c.fullChar
} }
// SetFullChar sets the rune used for 'full' // SetFullChar sets the rune used for 'full'
func (i *ProgressBar) SetFullChar(f rune) { func (c *ProgressBar) SetFullChar(f rune) {
i.fullChar = f c.fullChar = f
} }
// GetEmptyChar gets the rune used for 'empty' // GetEmptyChar gets the rune used for 'empty'
func (i *ProgressBar) GetEmptyChar() rune { func (c *ProgressBar) GetEmptyChar() rune {
return i.emptyChar return c.emptyChar
} }
// SetEmptyChar sets the rune used for 'empty' // SetEmptyChar sets the rune used for 'empty'
func (i *ProgressBar) SetEmptyChar(f rune) { func (c *ProgressBar) SetEmptyChar(f rune) {
i.emptyChar = f c.emptyChar = f
} }
// GetX Return the x position of the Progress Bar // 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 // SetX set the x position of the ProgressBar to x
func (i *ProgressBar) SetX(x int) { func (c *ProgressBar) SetX(x int) {
i.x = x c.x = x
} }
// GetY Return the y position of the ProgressBar // 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 // SetY Set the y position of the ProgressBar to y
func (i *ProgressBar) SetY(y int) { func (c *ProgressBar) SetY(y int) {
i.y = y c.y = y
} }
// GetHeight returns the height of the progress bar // GetHeight returns the height of the progress bar
// Defaults to 1 (3 if bordered) // Defaults to 1 (3 if bordered)
func (i *ProgressBar) GetHeight() int { func (c *ProgressBar) GetHeight() int {
return i.height return c.height
} }
// SetHeight Sets the height of the progress bar // SetHeight Sets the height of the progress bar
func (i *ProgressBar) SetHeight(h int) { func (c *ProgressBar) SetHeight(h int) {
i.height = h c.height = h
} }
// GetWidth returns the width of the progress bar // GetWidth returns the width of the progress bar
func (i *ProgressBar) GetWidth() int { func (c *ProgressBar) GetWidth() int {
return i.width return c.width
} }
// SetWidth Sets the width of the progress bar // SetWidth Sets the width of the progress bar
func (i *ProgressBar) SetWidth(w int) { func (c *ProgressBar) SetWidth(w int) {
i.width = w c.width = w
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *ProgressBar) SetFgColor(fg termbox.Attribute) { func (c *ProgressBar) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *ProgressBar) SetBgColor(bg termbox.Attribute) { func (c *ProgressBar) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
// Align Tells which direction the progress bar empties // Align Tells which direction the progress bar empties
func (i *ProgressBar) Align(a TextAlignment) { func (c *ProgressBar) Align(a TextAlignment) {
i.alignment = a c.alignment = a
} }
// SetColorized sets whether the progress bar should be colored // SetColorized sets whether the progress bar should be colored
@ -173,48 +181,48 @@ func (i *ProgressBar) Align(a TextAlignment) {
// 10% - Red // 10% - Red
// 50% - Yellow // 50% - Yellow
// 80% - Green // 80% - Green
func (i *ProgressBar) SetColorized(c bool) { func (c *ProgressBar) SetColorized(color bool) {
i.colorized = c c.colorized = color
} }
// HandleEvent accepts the termbox event and returns whether it was consumed // 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 return false
} }
// Draw outputs the input field on the screen // Draw outputs the input field on the screen
func (i *ProgressBar) Draw() { func (c *ProgressBar) Draw() {
// For now, just draw a [#### ] bar // For now, just draw a [#### ] bar
// TODO: make this more advanced // TODO: make this more advanced
useFg := i.fg useFg := c.fg
if i.colorized { if c.colorized {
if i.GetPercent() < 10 { if c.GetPercent() < 10 {
useFg = termbox.ColorRed useFg = termbox.ColorRed
} else if i.GetPercent() < 50 { } else if c.GetPercent() < 50 {
useFg = termbox.ColorYellow useFg = termbox.ColorYellow
} else { } else {
useFg = termbox.ColorGreen useFg = termbox.ColorGreen
} }
} }
drawX, drawY := i.x, i.y drawX, drawY := c.x, c.y
fillWidth, fillHeight := i.width-2, i.height fillWidth, fillHeight := c.width-2, c.height
DrawStringAtPoint("[", drawX, drawY, i.fg, i.bg) DrawStringAtPoint("[", drawX, drawY, c.fg, c.bg)
numFull := int(float64(fillWidth) * float64(i.progress) / float64(i.total)) numFull := int(float64(fillWidth) * float64(c.progress) / float64(c.total))
FillWithChar(i.fullChar, drawX+1, drawY, drawX+1+numFull, drawY+(fillHeight-1), useFg, i.bg) FillWithChar(c.fullChar, drawX+1, drawY, drawX+1+numFull, drawY+(fillHeight-1), useFg, c.bg)
DrawStringAtPoint("]", drawX+i.width-1, drawY, i.fg, i.bg) DrawStringAtPoint("]", drawX+c.width-1, drawY, c.fg, c.bg)
/* /*
drawX, drawY := i.x, i.y drawX, drawY := c.x, c.y
drawWidth, drawHeight := i.width, i.height drawWidth, drawHeight := c.width, c.height
if i.bordered { if c.bordered {
if i.height == 1 && i.width > 2 { if c.height == 1 && c.width > 2 {
// Just using [ & ] for the border // Just using [ & ] for the border
DrawStringAtPoint("[", drawX, drawY, i.fg, i.bg) DrawStringAtPoint("[", drawX, drawY, c.fg, c.bg)
DrawStringAtPoint("]", drawX+i.width-1, drawY, i.fg, i.bg) DrawStringAtPoint("]", drawX+c.width-1, drawY, c.fg, c.bg)
drawX++ drawX++
drawWidth -= 2 drawWidth -= 2
} else if i.height >= 3 { } else if c.height >= 3 {
DrawBorder(drawX, drawY, drawX+i.width, drawY+i.height, i.fg, i.bg) DrawBorder(drawX, drawY, drawX+c.width, drawY+c.height, c.fg, c.bg)
drawX++ drawX++
drawY++ drawY++
drawWidth -= 2 drawWidth -= 2
@ -223,14 +231,14 @@ func (i *ProgressBar) Draw() {
} }
// Figure out how many chars are full // Figure out how many chars are full
numFull := drawWidth * (i.progress / i.total) numFull := drawWidth * (c.progress / c.total)
switch i.alignment { switch c.alignment {
case AlignRight: // TODO: Fill from right to left case AlignRight: // TODO: Fill from right to left
case AlignCenter: // TODO: Fill from middle out case AlignCenter: // TODO: Fill from middle out
default: // Fill from left to right 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 { 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 scrollX, scrollY int
tabIdx int tabIdx int
fg, bg termbox.Attribute fg, bg termbox.Attribute
activeFg, activeBg termbox.Attribute
bordered bool bordered bool
controls []termboxControl controls []termboxControl
active bool
} }
// CreateScrollFrame creates Scrolling Frame at x, y that is w by h // CreateScrollFrame creates Scrolling Frame at x, y that is w by h
func CreateScrollFrame(x, y, w, h int, fg, bg termbox.Attribute) *ScrollFrame { 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} c := ScrollFrame{
return &s x: x, y: y, width: w, height: h,
fg: fg, bg: bg, activeFg: fg, activeBg: bg,
}
return &c
} }
// GetID returns this control's ID // 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 // SetID sets this control's ID
func (i *ScrollFrame) SetID(newID string) { func (c *ScrollFrame) SetID(newID string) {
i.id = newID c.id = newID
} }
// GetX returns the x position of the scroll frame // 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 // SetX sets the x position of the scroll frame
func (i *ScrollFrame) SetX(x int) { func (c *ScrollFrame) SetX(x int) {
i.x = x c.x = x
} }
// GetY returns the y position of the scroll frame // 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 // SetY sets the y position of the scroll frame
func (i *ScrollFrame) SetY(y int) { func (c *ScrollFrame) SetY(y int) {
i.y = y c.y = y
} }
// GetWidth returns the current width of the scroll frame // 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 // SetWidth sets the current width of the scroll frame
func (i *ScrollFrame) SetWidth(w int) { func (c *ScrollFrame) SetWidth(w int) {
i.width = w c.width = w
} }
// GetHeight returns the current height of the scroll frame // 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 // SetHeight sets the current height of the scroll frame
func (i *ScrollFrame) SetHeight(h int) { func (c *ScrollFrame) SetHeight(h int) {
i.height = h c.height = h
} }
// GetFgColor returns the foreground color // 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 // SetFgColor sets the foreground color
func (i *ScrollFrame) SetFgColor(fg termbox.Attribute) { func (c *ScrollFrame) SetFgColor(fg termbox.Attribute) {
i.fg = fg c.fg = fg
} }
// GetBgColor returns the background color // 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 // SetBgColor sets the current background color
func (i *ScrollFrame) SetBgColor(bg termbox.Attribute) { func (c *ScrollFrame) SetBgColor(bg termbox.Attribute) {
i.bg = bg c.bg = bg
} }
// IsBordered returns true or false if this scroll frame has a border // 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 // SetBordered sets whether we render a border around the scroll frame
func (i *ScrollFrame) SetBordered(b bool) { func (c *ScrollFrame) SetBordered(b bool) {
i.bordered = b c.bordered = b
} }
// GetScrollX returns the x distance scrolled // GetScrollX returns the x distance scrolled
func (i *ScrollFrame) GetScrollX() int { func (c *ScrollFrame) GetScrollX() int {
return i.scrollX return c.scrollX
} }
// GetScrollY returns the y distance scrolled // GetScrollY returns the y distance scrolled
func (i *ScrollFrame) GetScrollY() int { func (c *ScrollFrame) GetScrollY() int {
return i.scrollY return c.scrollY
} }
// ScrollDown scrolls the frame down // ScrollDown scrolls the frame down
func (i *ScrollFrame) ScrollDown() { func (c *ScrollFrame) ScrollDown() {
i.scrollY++ c.scrollY++
} }
// ScrollUp scrolls the frame up // ScrollUp scrolls the frame up
func (i *ScrollFrame) ScrollUp() { func (c *ScrollFrame) ScrollUp() {
if i.scrollY > 0 { if c.scrollY > 0 {
i.scrollY-- c.scrollY--
} }
} }
// ScrollLeft scrolls the frame left // ScrollLeft scrolls the frame left
func (i *ScrollFrame) ScrollLeft() { func (c *ScrollFrame) ScrollLeft() {
if i.scrollX > 0 { if c.scrollX > 0 {
i.scrollX-- c.scrollX--
} }
} }
// ScrollRight scrolls the frame right // ScrollRight scrolls the frame right
func (i *ScrollFrame) ScrollRight() { func (c *ScrollFrame) ScrollRight() {
i.scrollX++ c.scrollX++
} }
// AddControl adds a control to the frame // AddControl adds a control to the frame
func (i *ScrollFrame) AddControl(t termboxControl) { func (c *ScrollFrame) AddControl(t termboxControl) {
i.controls = append(i.controls, t) c.controls = append(c.controls, t)
} }
// DrawControl figures out the relative position of the control, // DrawControl figures out the relative position of the control,
// sets it, draws it, then resets it. // sets it, draws it, then resets it.
func (i *ScrollFrame) DrawControl(t termboxControl) { func (c *ScrollFrame) DrawControl(t termboxControl) {
if i.IsVisible(t) { if c.IsVisible(t) {
ctlX, ctlY := t.GetX(), t.GetY() ctlX, ctlY := t.GetX(), t.GetY()
t.SetX((i.GetX() + ctlX)) t.SetX((c.GetX() + ctlX))
t.SetY((i.GetY() + ctlY)) t.SetY((c.GetY() + ctlY))
t.Draw() t.Draw()
t.SetX(ctlX) t.SetX(ctlX)
t.SetY(ctlY) t.SetY(ctlY)
@ -138,35 +143,35 @@ func (i *ScrollFrame) DrawControl(t termboxControl) {
// IsVisible takes a Termbox Control and returns whether // IsVisible takes a Termbox Control and returns whether
// that control would be visible in the frame // 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 // Check if any part of t should be visible
cX, cY := t.GetX(), t.GetY() cX, cY := t.GetX(), t.GetY()
if cX+t.GetWidth() >= i.scrollX && cX <= i.scrollX+i.width { if cX+t.GetWidth() >= c.scrollX && cX <= c.scrollX+c.width {
return cY+t.GetHeight() >= i.scrollY && cY <= i.scrollY+i.height return cY+t.GetHeight() >= c.scrollY && cY <= c.scrollY+c.height
} }
return false return false
} }
// HandleEvent accepts the termbox event and returns whether it was consumed // 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 return false
} }
// DrawToStrings generates a slice of strings with what should // DrawToStrings generates a slice of strings with what should
// be drawn to the screen // be drawn to the screen
func (i *ScrollFrame) DrawToStrings() []string { func (c *ScrollFrame) DrawToStrings() []string {
return []string{} return []string{}
} }
// Draw outputs the Scoll Frame on the screen // Draw outputs the Scoll Frame on the screen
func (i *ScrollFrame) Draw() { func (c *ScrollFrame) Draw() {
maxWidth := i.width maxWidth := c.width
maxHeight := i.height maxHeight := c.height
x, y := i.x, i.y x, y := c.x, c.y
startX := i.x startX := c.x
startY := i.y startY := c.y
if i.bordered { if c.bordered {
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)
maxWidth-- maxWidth--
maxHeight-- maxHeight--
x++ x++
@ -174,7 +179,7 @@ func (i *ScrollFrame) Draw() {
startX++ startX++
startY++ startY++
} }
for idx := range i.controls { for idx := range c.controls {
i.DrawControl(i.controls[idx]) c.DrawControl(c.controls[idx])
} }
} }

View File

@ -1,6 +1,7 @@
package termboxUtil package termboxUtil
import ( import (
"errors"
"fmt" "fmt"
"strings" "strings"
@ -27,6 +28,10 @@ type termboxControl interface {
SetTabSkip(bool) SetTabSkip(bool)
IsTabSkipped() bool IsTabSkipped() bool
Draw() Draw()
SetActive(bool)
IsActive() bool
SetActiveFgColor(termbox.Attribute)
SetActiveBgColor(termbox.Attribute)
} }
// TextAlignment is an int value for how we're aligning text // 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 // 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) { func DrawBorder(x1, y1, x2, y2 int, fg, bg termbox.Attribute) {
termbox.SetCell(x1, y1, '+', fg, bg) termbox.SetCell(x1, y1, '', fg, bg)
FillWithChar('-', x1+1, y1, x2-1, y1, fg, bg) FillWithChar('', x1+1, y1, x2-1, y1, fg, bg)
termbox.SetCell(x2, y1, '+', fg, bg) termbox.SetCell(x2, y1, '', fg, bg)
FillWithChar('|', x1, y1+1, x1, y2-1, fg, bg) FillWithChar('', x1, y1+1, x1, y2-1, fg, bg)
FillWithChar('|', x2, y1+1, x2, y2-1, fg, bg) FillWithChar('', x2, y1+1, x2, y2-1, fg, bg)
termbox.SetCell(x1, y2, '+', fg, bg) termbox.SetCell(x1, y2, '╚', fg, bg)
FillWithChar('-', x1+1, y2, x2-1, y2, fg, bg) FillWithChar('═', x1+1, y2, x2-1, y2, fg, bg)
termbox.SetCell(x2, 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 // 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 */ /* More advanced things are in their respective files */