From c168c0df31ca080a787dbb4b5ca341517cd07499 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Mon, 25 Mar 2019 10:10:25 -0500 Subject: [PATCH] Just some stuff --- termbox-test/main.go | 4 +- termbox_alertmodal.go | 8 + termbox_asciiart.go | 20 +- termbox_button.go | 59 ++++++ termbox_confirmmodal.go | 5 +- termbox_dropmenu.go | 165 +++++++-------- termbox_frame.go | 218 +++++++++++++------- termbox_inputfield.go | 210 +++++++++++--------- termbox_inputmodal.go | 209 +++++++++---------- termbox_label.go | 116 +++++------ termbox_menu.go | 431 ++++++++++++++++++++++------------------ termbox_progressbar.go | 178 +++++++++-------- termbox_scrollframe.go | 127 ++++++------ termbox_util.go | 88 +++++++- 14 files changed, 1072 insertions(+), 766 deletions(-) create mode 100644 termbox_button.go diff --git a/termbox-test/main.go b/termbox-test/main.go index 9207095..fe2ea1f 100755 --- a/termbox-test/main.go +++ b/termbox-test/main.go @@ -5,7 +5,7 @@ import ( "strconv" "syscall" - "gogs.bullercodeworks.com/brian/termbox-util" + "github.com/br0xen/termbox-util" "github.com/nsf/termbox-go" ) @@ -115,4 +115,4 @@ func handleEvent(event termbox.Event) bool { } frame.GetActiveControl().SetFgColor(termbox.ColorGreen) return true -} \ No newline at end of file +} diff --git a/termbox_alertmodal.go b/termbox_alertmodal.go index ea6ec99..593aa3a 100644 --- a/termbox_alertmodal.go +++ b/termbox_alertmodal.go @@ -13,17 +13,20 @@ type AlertModal struct { showHelp bool cursor int bg, fg termbox.Attribute + activeFg, activeBg termbox.Attribute isDone bool accepted bool value string isVisible bool bordered bool tabSkip bool + active bool } // CreateAlertModal Creates a confirmation modal with the specified attributes func CreateAlertModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *AlertModal { i := AlertModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, bordered: true} + i.activeFg, i.activeBg = fg, bg if i.title == "" { i.title = "Alert!" } @@ -31,6 +34,11 @@ func CreateAlertModal(title string, x, y, width, height int, fg, bg termbox.Attr return &i } +func (i *AlertModal) SetActiveFgColor(fg termbox.Attribute) { i.activeFg = fg } +func (i *AlertModal) SetActiveBgColor(bg termbox.Attribute) { i.activeBg = bg } +func (i *AlertModal) SetActive(a bool) { i.active = a } +func (i *AlertModal) IsActive() bool { return i.active } + // GetID returns this control's ID func (i *AlertModal) GetID() string { return i.id } diff --git a/termbox_asciiart.go b/termbox_asciiart.go index 4f0746e..7ee2981 100644 --- a/termbox_asciiart.go +++ b/termbox_asciiart.go @@ -8,17 +8,20 @@ import ( // ASCIIArt is a []string with more functions type ASCIIArt struct { - id string - contents []string - x, y int - bg, fg termbox.Attribute - bordered bool - tabSkip bool + id string + contents []string + x, y int + bg, fg termbox.Attribute + activeFg, activeBg termbox.Attribute + bordered bool + tabSkip bool + active bool } // CreateASCIIArt Create an ASCII art object from a string slice func CreateASCIIArt(c []string, x, y int, fg, bg termbox.Attribute) *ASCIIArt { i := ASCIIArt{contents: c, x: x, y: y, fg: fg, bg: bg, bordered: false, tabSkip: true} + i.activeFg, i.activeBg = fg, bg return &i } @@ -87,6 +90,11 @@ func (i *ASCIIArt) SetWidth(w int) { } } +func (i *ASCIIArt) SetActiveFgColor(fg termbox.Attribute) { i.activeFg = fg } +func (i *ASCIIArt) SetActiveBgColor(bg termbox.Attribute) { i.activeBg = bg } +func (i *ASCIIArt) SetActive(a bool) { i.active = a } +func (i *ASCIIArt) IsActive() bool { return i.active } + // SetContents Sets the contents of i to c func (i *ASCIIArt) SetContents(c []string) { i.contents = c diff --git a/termbox_button.go b/termbox_button.go new file mode 100644 index 0000000..b65c4a6 --- /dev/null +++ b/termbox_button.go @@ -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) +} diff --git a/termbox_confirmmodal.go b/termbox_confirmmodal.go index 9a22fd6..1f07bc7 100644 --- a/termbox_confirmmodal.go +++ b/termbox_confirmmodal.go @@ -12,7 +12,8 @@ type ConfirmModal struct { x, y, width, height int showHelp bool cursor int - bg, fg termbox.Attribute + fg, bg termbox.Attribute + activeFg, activeBg termbox.Attribute isDone bool accepted bool value string @@ -23,7 +24,7 @@ type ConfirmModal struct { // CreateConfirmModal Creates a confirmation modal with the specified attributes func CreateConfirmModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *ConfirmModal { - i := ConfirmModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg} + i := ConfirmModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, activeFg: fg, activeBg: bg} if i.title == "" && i.text == "" { i.title = "Confirm?" } diff --git a/termbox_dropmenu.go b/termbox_dropmenu.go index 3d755d7..f672bb7 100644 --- a/termbox_dropmenu.go +++ b/termbox_dropmenu.go @@ -4,174 +4,181 @@ import "github.com/nsf/termbox-go" // DropMenu is a title that, when active drops a menu down type DropMenu struct { - id string - title string - x, y, width, height int - bg, fg termbox.Attribute - selectedBg, selectedFg termbox.Attribute - menu *Menu - menuSelected bool - showMenu bool - bordered bool - tabSkip bool + id string + title string + x, y, width, height int + bg, fg termbox.Attribute + activeFg, activeBg termbox.Attribute + cursorBg, cursorFg termbox.Attribute + menu *Menu + menuSelected bool + showMenu bool + bordered bool + tabSkip bool + active bool } // CreateDropMenu Creates a menu with the specified attributes -func CreateDropMenu(title string, options []string, x, y, width, height int, fg, bg, selectedFg, selectedBg termbox.Attribute) *DropMenu { - i := DropMenu{ +func CreateDropMenu(title string, options []string, x, y, width, height int, fg, bg, cursorFg, cursorBg termbox.Attribute) *DropMenu { + c := DropMenu{ title: title, x: x, y: y, width: width, height: height, - fg: fg, bg: bg, - selectedFg: fg, selectedBg: bg, + fg: fg, bg: bg, activeFg: fg, activeBg: bg, + cursorFg: fg, cursorBg: bg, } - i.menu = CreateMenu("", options, x, y+2, width, height, fg, bg) - return &i + c.menu = CreateMenu("", options, x, y+2, width, height, fg, bg) + return &c } // GetID returns this control's ID -func (i *DropMenu) GetID() string { return i.id } +func (c *DropMenu) GetID() string { return c.id } // SetID sets this control's ID -func (i *DropMenu) SetID(newID string) { - i.id = newID +func (c *DropMenu) SetID(newID string) { + c.id = newID } +func (c *DropMenu) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg } +func (c *DropMenu) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg } +func (c *DropMenu) SetActive(a bool) { c.active = a } +func (c *DropMenu) IsActive() bool { return c.active } + // GetTitle returns the current title of the menu -func (i *DropMenu) GetTitle() string { return i.title } +func (c *DropMenu) GetTitle() string { return c.title } // SetTitle sets the current title of the menu to s -func (i *DropMenu) SetTitle(s string) { - i.title = s +func (c *DropMenu) SetTitle(s string) { + c.title = s } // GetMenu returns the menu for this dropmenu -func (i *DropMenu) GetMenu() *Menu { - return i.menu +func (c *DropMenu) GetMenu() *Menu { + return c.menu } // GetX returns the current x coordinate of the menu -func (i *DropMenu) GetX() int { return i.x } +func (c *DropMenu) GetX() int { return c.x } // SetX sets the current x coordinate of the menu to x -func (i *DropMenu) SetX(x int) { - i.x = x +func (c *DropMenu) SetX(x int) { + c.x = x } // GetY returns the current y coordinate of the menu -func (i *DropMenu) GetY() int { return i.y } +func (c *DropMenu) GetY() int { return c.y } // SetY sets the current y coordinate of the menu to y -func (i *DropMenu) SetY(y int) { - i.y = y +func (c *DropMenu) SetY(y int) { + c.y = y } // GetWidth returns the current width of the menu -func (i *DropMenu) GetWidth() int { return i.width } +func (c *DropMenu) GetWidth() int { return c.width } // SetWidth sets the current menu width to width -func (i *DropMenu) SetWidth(width int) { - i.width = width +func (c *DropMenu) SetWidth(width int) { + c.width = width } // GetHeight returns the current height of the menu -func (i *DropMenu) GetHeight() int { return i.height } +func (c *DropMenu) GetHeight() int { return c.height } // SetHeight set the height of the menu to height -func (i *DropMenu) SetHeight(height int) { - i.height = height +func (c *DropMenu) SetHeight(height int) { + c.height = height } // GetFgColor returns the foreground color -func (i *DropMenu) GetFgColor() termbox.Attribute { return i.fg } +func (c *DropMenu) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *DropMenu) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *DropMenu) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *DropMenu) GetBgColor() termbox.Attribute { return i.bg } +func (c *DropMenu) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *DropMenu) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *DropMenu) SetBgColor(bg termbox.Attribute) { + c.bg = bg } // IsBordered returns the bordered flag -func (i *DropMenu) IsBordered() bool { return i.bordered } +func (c *DropMenu) IsBordered() bool { return c.bordered } // SetBordered sets the bordered flag -func (i *DropMenu) SetBordered(b bool) { - i.bordered = b - i.menu.SetBordered(b) +func (c *DropMenu) SetBordered(b bool) { + c.bordered = b + c.menu.SetBordered(b) } // IsDone returns whether the user has answered the modal -func (i *DropMenu) IsDone() bool { return i.menu.isDone } +func (c *DropMenu) IsDone() bool { return c.menu.isDone } // SetDone sets whether the modal has completed it's purpose -func (i *DropMenu) SetDone(b bool) { - i.menu.isDone = b +func (c *DropMenu) SetDone(b bool) { + c.menu.isDone = b } // IsTabSkipped returns whether this modal has it's tabskip flag set -func (i *DropMenu) IsTabSkipped() bool { - return i.tabSkip +func (c *DropMenu) IsTabSkipped() bool { + return c.tabSkip } // SetTabSkip sets the tabskip flag for this control -func (i *DropMenu) SetTabSkip(b bool) { - i.tabSkip = b +func (c *DropMenu) SetTabSkip(b bool) { + c.tabSkip = b } // ShowMenu tells the menu to draw the options -func (i *DropMenu) ShowMenu() { - i.showMenu = true - i.menuSelected = true +func (c *DropMenu) ShowMenu() { + c.showMenu = true + c.menuSelected = true } // HideMenu tells the menu to hide the options -func (i *DropMenu) HideMenu() { - i.showMenu = false - i.menuSelected = false +func (c *DropMenu) HideMenu() { + c.showMenu = false + c.menuSelected = false } // HandleEvent handles the termbox event and returns whether it was consumed -func (i *DropMenu) HandleEvent(event termbox.Event) bool { - moveUp := (event.Key == termbox.KeyArrowUp || (i.menu.vimMode && event.Ch == 'k')) - moveDown := (event.Key == termbox.KeyArrowDown || (i.menu.vimMode && event.Ch == 'j')) - if i.menuSelected { - selIdx := i.menu.GetSelectedIndex() - if (moveUp && selIdx == 0) || (moveDown && selIdx == (len(i.menu.options)-1)) { - i.menuSelected = false +func (c *DropMenu) HandleEvent(event termbox.Event) bool { + moveUp := (event.Key == termbox.KeyArrowUp || (c.menu.vimMode && event.Ch == 'k')) + moveDown := (event.Key == termbox.KeyArrowDown || (c.menu.vimMode && event.Ch == 'j')) + if c.menuSelected { + selIdx := c.menu.GetSelectedIndex() + if (moveUp && selIdx == 0) || (moveDown && selIdx == (len(c.menu.options)-1)) { + c.menuSelected = false } else { - if i.menu.HandleEvent(event) { - if i.menu.IsDone() { - i.HideMenu() + if c.menu.HandleEvent(event) { + if c.menu.IsDone() { + c.HideMenu() } return true } } } else { - i.ShowMenu() + c.ShowMenu() return true } return false } // Draw draws the menu -func (i *DropMenu) Draw() { +func (c *DropMenu) Draw() { // The title - ttlFg, ttlBg := i.fg, i.bg - if !i.menuSelected { - ttlFg, ttlBg = i.selectedFg, i.selectedBg + ttlFg, ttlBg := c.fg, c.bg + if !c.menuSelected { + ttlFg, ttlBg = c.cursorFg, c.cursorBg } - ttlTxt := i.title - if i.showMenu { + ttlTxt := c.title + if c.showMenu { ttlTxt = ttlTxt + "-Showing Menu" } - DrawStringAtPoint(AlignText(i.title, i.width, AlignLeft), i.x, i.y, ttlFg, ttlBg) - if i.showMenu { - i.menu.Draw() + DrawStringAtPoint(AlignText(c.title, c.width, AlignLeft), c.x, c.y, ttlFg, ttlBg) + if c.showMenu { + c.menu.Draw() } } diff --git a/termbox_frame.go b/termbox_frame.go index 747201f..fbad130 100644 --- a/termbox_frame.go +++ b/termbox_frame.go @@ -9,187 +9,252 @@ type Frame struct { x, y, width, height int tabIdx int fg, bg termbox.Attribute + activeFg, activeBg termbox.Attribute bordered bool controls []termboxControl tabSkip bool + active bool + title string } // CreateFrame creates a Frame at x, y that is w by h func CreateFrame(x, y, w, h int, fg, bg termbox.Attribute) *Frame { - s := Frame{x: x, y: y, width: w, height: h, fg: fg, bg: bg, bordered: true} - return &s + c := Frame{x: x, y: y, width: w, height: h, + fg: fg, bg: bg, activeFg: fg, activeBg: bg, + bordered: true, + } + return &c } +func (c *Frame) SetTitle(title string) { c.title = title } + +// Setting color attributes on a frame trickles down to its controls +func (c *Frame) SetActiveFgColor(fg termbox.Attribute) { + c.activeFg = fg + for _, v := range c.controls { + v.SetActiveFgColor(fg) + } +} +func (c *Frame) SetActiveBgColor(bg termbox.Attribute) { + c.activeBg = bg + for _, v := range c.controls { + v.SetActiveBgColor(bg) + } +} +func (c *Frame) SetActive(a bool) { + c.active = a + for idx := range c.controls { + if idx == c.tabIdx && a { + c.controls[idx].SetActive(true) + } else { + c.controls[idx].SetActive(false) + } + } +} +func (c *Frame) IsActive() bool { return c.active } + // GetID returns this control's ID -func (i *Frame) GetID() string { return i.id } +func (c *Frame) GetID() string { return c.id } // SetID sets this control's ID -func (i *Frame) SetID(newID string) { - i.id = newID +func (c *Frame) SetID(newID string) { + c.id = newID } // GetX returns the x position of the frame -func (i *Frame) GetX() int { return i.x } +func (c *Frame) GetX() int { return c.x } // SetX sets the x position of the frame -func (i *Frame) SetX(x int) { - i.x = x +func (c *Frame) SetX(x int) { + c.x = x } // GetY returns the y position of the frame -func (i *Frame) GetY() int { return i.y } +func (c *Frame) GetY() int { return c.y } // SetY sets the y position of the frame -func (i *Frame) SetY(y int) { - i.y = y +func (c *Frame) SetY(y int) { + c.y = y } // GetWidth returns the current width of the frame -func (i *Frame) GetWidth() int { return i.width } +func (c *Frame) GetWidth() int { return c.width } // SetWidth sets the current width of the frame -func (i *Frame) SetWidth(w int) { - i.width = w +func (c *Frame) SetWidth(w int) { + c.width = w } // GetHeight returns the current height of the frame -func (i *Frame) GetHeight() int { return i.height } +func (c *Frame) GetHeight() int { return c.height } // SetHeight sets the current height of the frame -func (i *Frame) SetHeight(h int) { - i.height = h +func (c *Frame) SetHeight(h int) { + c.height = h } // GetFgColor returns the foreground color -func (i *Frame) GetFgColor() termbox.Attribute { return i.fg } +func (c *Frame) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *Frame) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *Frame) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *Frame) GetBgColor() termbox.Attribute { return i.bg } +func (c *Frame) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *Frame) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *Frame) SetBgColor(bg termbox.Attribute) { + c.bg = bg } // IsBordered returns true or false if this frame has a border -func (i *Frame) IsBordered() bool { return i.bordered } +func (c *Frame) IsBordered() bool { return c.bordered } // SetBordered sets whether we render a border around the frame -func (i *Frame) SetBordered(b bool) { - i.bordered = b +func (c *Frame) SetBordered(b bool) { + c.bordered = b } // IsTabSkipped returns whether this modal has it's tabskip flag set -func (i *Frame) IsTabSkipped() bool { - return i.tabSkip +func (c *Frame) IsTabSkipped() bool { + return c.tabSkip } // SetTabSkip sets the tabskip flag for this control -func (i *Frame) SetTabSkip(b bool) { - i.tabSkip = b +func (c *Frame) SetTabSkip(b bool) { + c.tabSkip = b } // AddControl adds a control to the frame -func (i *Frame) AddControl(t termboxControl) { - i.controls = append(i.controls, t) +func (c *Frame) AddControl(t termboxControl) { + c.controls = append(c.controls, t) +} + +func (c *Frame) ResetTabIndex() { + for k, v := range c.controls { + if !v.IsTabSkipped() { + c.tabIdx = k + break + } + } } // GetActiveControl returns the control at tabIdx -func (i *Frame) GetActiveControl() termboxControl { - if len(i.controls) >= i.tabIdx { - return i.controls[i.tabIdx] +func (c *Frame) GetActiveControl() termboxControl { + if len(c.controls) >= c.tabIdx { + if c.controls[c.tabIdx].IsTabSkipped() { + c.FindNextTabStop() + } + return c.controls[c.tabIdx] } return nil } // GetControls returns a slice of all controls -func (i *Frame) GetControls() []termboxControl { - return i.controls +func (c *Frame) GetControls() []termboxControl { + return c.controls } // GetControl returns the control at index i -func (i *Frame) GetControl(idx int) termboxControl { - if len(i.controls) >= idx { - return i.controls[idx] +func (c *Frame) GetControl(idx int) termboxControl { + if len(c.controls) >= idx { + return c.controls[idx] } return nil } // GetControlCount returns the number of controls contained -func (i *Frame) GetControlCount() int { - return len(i.controls) +func (c *Frame) GetControlCount() int { + return len(c.controls) } // GetLastControl returns the last control contained -func (i *Frame) GetLastControl() termboxControl { - return i.controls[len(i.controls)-1] +func (c *Frame) GetLastControl() termboxControl { + return c.controls[len(c.controls)-1] } // RemoveAllControls clears the control slice -func (i *Frame) RemoveAllControls() { - i.controls = []termboxControl{} +func (c *Frame) RemoveAllControls() { + c.controls = []termboxControl{} } // DrawControl figures out the relative position of the control, // sets it, draws it, then resets it. -func (i *Frame) DrawControl(t termboxControl) { +func (c *Frame) DrawControl(t termboxControl) { ctlX, ctlY := t.GetX(), t.GetY() - t.SetX((i.GetX() + ctlX)) - t.SetY((i.GetY() + ctlY)) + t.SetX((c.GetX() + ctlX)) + t.SetY((c.GetY() + ctlY)) t.Draw() t.SetX(ctlX) t.SetY(ctlY) } // GetBottomY returns the y of the lowest control in the frame -func (i *Frame) GetBottomY() int { +func (c *Frame) GetBottomY() int { var ret int - for idx := range i.controls { - if i.controls[idx].GetY()+i.controls[idx].GetHeight() > ret { - ret = i.controls[idx].GetY() + i.controls[idx].GetHeight() + for idx := range c.controls { + if c.controls[idx].GetY()+c.controls[idx].GetHeight() > ret { + ret = c.controls[idx].GetY() + c.controls[idx].GetHeight() } } return ret } // HandleEvent accepts the termbox event and returns whether it was consumed -func (i *Frame) HandleEvent(event termbox.Event) bool { +func (c *Frame) HandleEvent(event termbox.Event) bool { if event.Key == termbox.KeyTab { - i.FindNextTabStop() + c.FindNextTabStop() return true } - return i.controls[i.tabIdx].HandleEvent(event) + return c.controls[c.tabIdx].HandleEvent(event) } // FindNextTabStop finds the next control that can be tabbed to // A return of true means it found a different one than we started on. -func (i *Frame) FindNextTabStop() bool { - startTab := i.tabIdx - i.tabIdx = (i.tabIdx + 1) % len(i.controls) - for i.controls[i.tabIdx].IsTabSkipped() { - i.tabIdx = (i.tabIdx + 1) % len(i.controls) - if i.tabIdx == startTab { +func (c *Frame) FindNextTabStop() bool { + startTab := c.tabIdx + c.tabIdx = (c.tabIdx + 1) % len(c.controls) + for c.controls[c.tabIdx].IsTabSkipped() { + c.tabIdx = (c.tabIdx + 1) % len(c.controls) + if c.tabIdx == startTab { break } } - return i.tabIdx != startTab + return c.tabIdx != startTab +} + +// IsOnLastControl returns true if the active control +// is the last control that isn't tab skippable. +func (c *Frame) IsOnLastControl() bool { + for _, v := range c.controls[c.tabIdx+1:] { + if !v.IsTabSkipped() { + return false + } + } + return true } // Draw outputs the Scoll Frame on the screen -func (i *Frame) Draw() { - maxWidth := i.width - maxHeight := i.height - x, y := i.x, i.y - startX := i.x - startY := i.y - if i.bordered { - FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) - DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) +func (c *Frame) Draw() { + maxWidth := c.width + maxHeight := c.height + x, y := c.x, c.y + startX := c.x + startY := c.y + borderFg, borderBg := c.fg, c.bg + if c.active { + borderFg, borderBg = c.activeFg, c.activeBg + } + if c.bordered { + // Clear the framed area + FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, borderFg, borderBg) + if c.title == "" { + DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, borderFg, borderBg) + } else { + DrawBorderWithTitle(c.x, c.y, c.x+c.width, c.y+c.height, " "+c.title+" ", borderFg, borderBg) + } maxWidth-- maxHeight-- x++ @@ -197,7 +262,12 @@ func (i *Frame) Draw() { startX++ startY++ } - for idx := range i.controls { - i.DrawControl(i.controls[idx]) + for idx := range c.controls { + if idx == c.tabIdx { + c.controls[idx].SetActive(true) + } else { + c.controls[idx].SetActive(false) + } + c.DrawControl(c.controls[idx]) } } diff --git a/termbox_inputfield.go b/termbox_inputfield.go index e8dfcfe..574956f 100644 --- a/termbox_inputfield.go +++ b/termbox_inputfield.go @@ -5,153 +5,164 @@ import "github.com/nsf/termbox-go" // InputField is a field for inputting text type InputField struct { id string + title string value string x, y, width, height int cursor int fg, bg termbox.Attribute + activeFg, activeBg termbox.Attribute cursorFg, cursorBg termbox.Attribute bordered bool wrap bool multiline bool tabSkip bool + active bool } // CreateInputField creates an input field at x, y that is w by h func CreateInputField(x, y, w, h int, fg, bg termbox.Attribute) *InputField { - i := InputField{x: x, y: y, width: w, height: h, fg: fg, bg: bg, cursorFg: bg, cursorBg: fg} - return &i + c := InputField{x: x, y: y, width: w, height: h, + fg: fg, bg: bg, cursorFg: bg, cursorBg: fg, activeFg: fg, activeBg: bg, + } + return &c } +func (c *InputField) SetTitle(title string) { c.title = title } +func (c *InputField) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg } +func (c *InputField) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg } +func (c *InputField) SetActive(a bool) { c.active = a } +func (c *InputField) IsActive() bool { return c.active } + // GetID returns this control's ID -func (i *InputField) GetID() string { return i.id } +func (c *InputField) GetID() string { return c.id } // SetID sets this control's ID -func (i *InputField) SetID(newID string) { - i.id = newID +func (c *InputField) SetID(newID string) { + c.id = newID } // GetValue gets the current text that is in the InputField -func (i *InputField) GetValue() string { return i.value } +func (c *InputField) GetValue() string { return c.value } // SetValue sets the current text in the InputField to s -func (i *InputField) SetValue(s string) { - i.value = s +func (c *InputField) SetValue(s string) { + c.value = s } // GetX returns the x position of the input field -func (i *InputField) GetX() int { return i.x } +func (c *InputField) GetX() int { return c.x } // SetX sets the x position of the input field -func (i *InputField) SetX(x int) { - i.x = x +func (c *InputField) SetX(x int) { + c.x = x } // GetY returns the y position of the input field -func (i *InputField) GetY() int { return i.y } +func (c *InputField) GetY() int { return c.y } // SetY sets the y position of the input field -func (i *InputField) SetY(y int) { - i.y = y +func (c *InputField) SetY(y int) { + c.y = y } // GetWidth returns the current width of the input field -func (i *InputField) GetWidth() int { return i.width } +func (c *InputField) GetWidth() int { return c.width } // SetWidth sets the current width of the input field -func (i *InputField) SetWidth(w int) { - i.width = w +func (c *InputField) SetWidth(w int) { + c.width = w } // GetHeight returns the current height of the input field -func (i *InputField) GetHeight() int { return i.height } +func (c *InputField) GetHeight() int { return c.height } // SetHeight sets the current height of the input field -func (i *InputField) SetHeight(h int) { - i.height = h +func (c *InputField) SetHeight(h int) { + c.height = h } // GetFgColor returns the foreground color -func (i *InputField) GetFgColor() termbox.Attribute { return i.fg } +func (c *InputField) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *InputField) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *InputField) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *InputField) GetBgColor() termbox.Attribute { return i.bg } +func (c *InputField) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *InputField) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *InputField) SetBgColor(bg termbox.Attribute) { + c.bg = bg } -func (i *InputField) SetCursorFg(fg termbox.Attribute) { - i.cursorFg = fg +func (c *InputField) SetCursorFg(fg termbox.Attribute) { + c.cursorFg = fg } -func (i *InputField) GetCursorFg() termbox.Attribute { return i.cursorFg } +func (c *InputField) GetCursorFg() termbox.Attribute { return c.cursorFg } -func (i *InputField) SetCursorBg(bg termbox.Attribute) { - i.cursorBg = bg +func (c *InputField) SetCursorBg(bg termbox.Attribute) { + c.cursorBg = bg } -func (i *InputField) GetCursorBg() termbox.Attribute { return i.cursorBg } +func (c *InputField) GetCursorBg() termbox.Attribute { return c.cursorBg } // IsBordered returns true or false if this input field has a border -func (i *InputField) IsBordered() bool { return i.bordered } +func (c *InputField) IsBordered() bool { return c.bordered } // SetBordered sets whether we render a border around the input field -func (i *InputField) SetBordered(b bool) { - i.bordered = b +func (c *InputField) SetBordered(b bool) { + c.bordered = b } // IsTabSkipped returns whether this modal has it's tabskip flag set -func (i *InputField) IsTabSkipped() bool { - return i.tabSkip +func (c *InputField) IsTabSkipped() bool { + return c.tabSkip } // SetTabSkip sets the tabskip flag for this control -func (i *InputField) SetTabSkip(b bool) { - i.tabSkip = b +func (c *InputField) SetTabSkip(b bool) { + c.tabSkip = b } // DoesWrap returns true or false if this input field wraps text -func (i *InputField) DoesWrap() bool { return i.wrap } +func (c *InputField) DoesWrap() bool { return c.wrap } // SetWrap sets whether we wrap the text at width. -func (i *InputField) SetWrap(b bool) { - i.wrap = b +func (c *InputField) SetWrap(b bool) { + c.wrap = b } // IsMultiline returns true or false if this field can have multiple lines -func (i *InputField) IsMultiline() bool { return i.multiline } +func (c *InputField) IsMultiline() bool { return c.multiline } // SetMultiline sets whether the field can have multiple lines -func (i *InputField) SetMultiline(b bool) { - i.multiline = b +func (c *InputField) SetMultiline(b bool) { + c.multiline = b } // HandleEvent accepts the termbox event and returns whether it was consumed -func (i *InputField) HandleEvent(event termbox.Event) bool { +func (c *InputField) HandleEvent(event termbox.Event) bool { if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 { - if i.cursor+len(i.value) > 0 { - crs := len(i.value) - if i.cursor < 0 { - crs = i.cursor + len(i.value) + if c.cursor+len(c.value) > 0 { + crs := len(c.value) + if c.cursor < 0 { + crs = c.cursor + len(c.value) } - i.value = i.value[:crs-1] + i.value[crs:] - //i.value = i.value[:len(i.value)-1] + c.value = c.value[:crs-1] + c.value[crs:] + //c.value = c.value[:len(c.value)-1] } } else if event.Key == termbox.KeyArrowLeft { - if i.cursor+len(i.value) > 0 { - i.cursor-- + if c.cursor+len(c.value) > 0 { + c.cursor-- } } else if event.Key == termbox.KeyArrowRight { - if i.cursor < 0 { - i.cursor++ + if c.cursor < 0 { + c.cursor++ } } else if event.Key == termbox.KeyCtrlU { // Ctrl+U Clears the Input (before the cursor) - i.value = i.value[i.cursor+len(i.value):] + c.value = c.value[c.cursor+len(c.value):] } else { // Get the rune to add to our value. Space and Tab are special cases where // we can't use the event's rune directly @@ -162,7 +173,7 @@ func (i *InputField) HandleEvent(event termbox.Event) bool { case termbox.KeyTab: ch = "\t" case termbox.KeyEnter: - if i.multiline { + if c.multiline { ch = "\n" } default: @@ -172,28 +183,32 @@ func (i *InputField) HandleEvent(event termbox.Event) bool { } // TODO: Handle newlines - if i.cursor+len(i.value) == 0 { - i.value = string(ch) + i.value - } else if i.cursor == 0 { - i.value = i.value + string(ch) + if c.cursor+len(c.value) == 0 { + c.value = string(ch) + c.value + } else if c.cursor == 0 { + c.value = c.value + string(ch) } else { - strPt1 := i.value[:(len(i.value) + i.cursor)] - strPt2 := i.value[(len(i.value) + i.cursor):] - i.value = strPt1 + string(ch) + strPt2 + strPt1 := c.value[:(len(c.value) + c.cursor)] + strPt2 := c.value[(len(c.value) + c.cursor):] + c.value = strPt1 + string(ch) + strPt2 } } return true } // Draw outputs the input field on the screen -func (i *InputField) Draw() { - maxWidth := i.width - maxHeight := i.height - x, y := i.x, i.y - startX := i.x - startY := i.y - if i.bordered { - DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) +func (c *InputField) Draw() { + maxWidth := c.width + maxHeight := c.height + x, y := c.x, c.y + startX := c.x + startY := c.y + useFg, useBg := c.fg, c.bg + if c.active { + useFg, useBg = c.activeFg, c.activeBg + } + if c.bordered { + DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, useFg, useBg) maxWidth-- maxHeight-- x++ @@ -204,53 +219,60 @@ func (i *InputField) Draw() { var strPt1, strPt2 string var cursorRune rune - if len(i.value) > 0 { - if i.cursor+len(i.value) == 0 { + if len(c.value) > 0 { + if c.cursor+len(c.value) == 0 { strPt1 = "" - strPt2 = i.value[1:] - cursorRune = rune(i.value[0]) - } else if i.cursor == 0 { - strPt1 = i.value + strPt2 = c.value[1:] + cursorRune = rune(c.value[0]) + } else if c.cursor == 0 { + strPt1 = c.value strPt2 = "" cursorRune = ' ' } else { - strPt1 = i.value[:(len(i.value) + i.cursor)] - strPt2 = i.value[(len(i.value)+i.cursor)+1:] - cursorRune = rune(i.value[len(i.value)+i.cursor]) + strPt1 = c.value[:(len(c.value) + c.cursor)] + strPt2 = c.value[(len(c.value)+c.cursor)+1:] + cursorRune = rune(c.value[len(c.value)+c.cursor]) } } else { strPt1, strPt2, cursorRune = "", "", ' ' } - if i.wrap { + if c.title != "" { + if c.active { + DrawStringAtPoint(c.title, x, y, c.activeFg, c.activeBg) + } else { + DrawStringAtPoint(c.title, x, y, useFg, useBg) + } + } + if c.wrap { // Split the text into maxWidth chunks for len(strPt1) > maxWidth { breakAt := maxWidth - DrawStringAtPoint(strPt1[:breakAt], x, y, i.fg, i.bg) + DrawStringAtPoint(strPt1[:breakAt], x, y, useFg, useBg) x = startX y++ strPt1 = strPt1[breakAt:] } - x, y = DrawStringAtPoint(strPt1, x, y, i.fg, i.bg) + x, y = DrawStringAtPoint(strPt1, x, y, useFg, useBg) if x >= maxWidth { y++ x = startX } - termbox.SetCell(x, y, cursorRune, i.cursorFg, i.cursorBg) + termbox.SetCell(x, y, cursorRune, c.cursorFg, c.cursorBg) x++ if len(strPt2) > 0 { lenLeft := maxWidth - len(strPt1) - 1 if lenLeft > 0 && len(strPt2) > lenLeft { - DrawStringAtPoint(strPt2[:lenLeft], x+1, y, i.fg, i.bg) + DrawStringAtPoint(strPt2[:lenLeft], x+1, y, useFg, useBg) strPt2 = strPt2[lenLeft:] } for len(strPt2) > maxWidth { breakAt := maxWidth - DrawStringAtPoint(strPt2[:breakAt], x, y, i.fg, i.bg) + DrawStringAtPoint(strPt2[:breakAt], x, y, useFg, useBg) x = startX y++ strPt2 = strPt2[breakAt:] } - x, y = DrawStringAtPoint(strPt2, x, y, i.fg, i.bg) + x, y = DrawStringAtPoint(strPt2, x, y, useFg, useBg) } } else { for len(strPt1)+len(strPt2)+1 > maxWidth { @@ -263,8 +285,12 @@ func (i *InputField) Draw() { strPt2 = strPt2[:len(strPt2)-1] } } - x, y = DrawStringAtPoint(strPt1, i.x+1, i.y+1, i.fg, i.bg) - termbox.SetCell(x, y, cursorRune, i.cursorFg, i.cursorBg) - DrawStringAtPoint(strPt2, x+1, y, i.fg, i.bg) + x, y = DrawStringAtPoint(strPt1, c.x+len(c.title), c.y, useFg, useBg) + if c.active { + termbox.SetCell(x, y, cursorRune, c.cursorFg, c.cursorBg) + } else { + termbox.SetCell(x, y, cursorRune, useFg, useBg) + } + DrawStringAtPoint(strPt2, x+1, y, useFg, useBg) } } diff --git a/termbox_inputmodal.go b/termbox_inputmodal.go index 4268314..e94ea36 100644 --- a/termbox_inputmodal.go +++ b/termbox_inputmodal.go @@ -14,244 +14,251 @@ type InputModal struct { showHelp bool cursor int bg, fg termbox.Attribute + activeFg, activeBg termbox.Attribute isDone bool isAccepted bool isVisible bool bordered bool tabSkip bool inputSelected bool + active bool } // CreateInputModal Create an input modal with the given attributes func CreateInputModal(title string, x, y, width, height int, fg, bg termbox.Attribute) *InputModal { - i := InputModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, bordered: true} - i.input = CreateInputField(i.x+2, i.y+3, i.width-2, 2, i.fg, i.bg) - i.showHelp = true - i.input.bordered = true - i.isVisible = true - i.inputSelected = true - return &i + c := InputModal{title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, bordered: true} + c.input = CreateInputField(c.x+2, c.y+3, c.width-2, 2, c.fg, c.bg) + c.showHelp = true + c.input.bordered = true + c.isVisible = true + c.inputSelected = true + return &c } +func (c *InputModal) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg } +func (c *InputModal) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg } +func (c *InputModal) SetActive(a bool) { c.active = a } +func (c *InputModal) IsActive() bool { return c.active } + // GetID returns this control's ID -func (i *InputModal) GetID() string { return i.id } +func (c *InputModal) GetID() string { return c.id } // SetID sets this control's ID -func (i *InputModal) SetID(newID string) { - i.id = newID +func (c *InputModal) SetID(newID string) { + c.id = newID } // GetTitle Return the title of the modal -func (i *InputModal) GetTitle() string { return i.title } +func (c *InputModal) GetTitle() string { return c.title } // SetTitle Sets the title of the modal to s -func (i *InputModal) SetTitle(s string) { - i.title = s +func (c *InputModal) SetTitle(s string) { + c.title = s } // GetText Return the text of the modal -func (i *InputModal) GetText() string { return i.text } +func (c *InputModal) GetText() string { return c.text } // SetText Set the text of the modal to s -func (i *InputModal) SetText(s string) { - i.text = s +func (c *InputModal) SetText(s string) { + c.text = s } // GetX Return the x position of the modal -func (i *InputModal) GetX() int { return i.x } +func (c *InputModal) GetX() int { return c.x } // SetX set the x position of the modal to x -func (i *InputModal) SetX(x int) { - i.x = x +func (c *InputModal) SetX(x int) { + c.x = x } // GetY Return the y position of the modal -func (i *InputModal) GetY() int { return i.y } +func (c *InputModal) GetY() int { return c.y } // SetY Set the y position of the modal to y -func (i *InputModal) SetY(y int) { - i.y = y +func (c *InputModal) SetY(y int) { + c.y = y } // GetWidth Return the width of the modal -func (i *InputModal) GetWidth() int { return i.width } +func (c *InputModal) GetWidth() int { return c.width } // SetWidth Set the width of the modal to width -func (i *InputModal) SetWidth(width int) { - i.width = width +func (c *InputModal) SetWidth(width int) { + c.width = width } // GetHeight Return the height of the modal -func (i *InputModal) GetHeight() int { return i.height } +func (c *InputModal) GetHeight() int { return c.height } // SetHeight Set the height of the modal to height -func (i *InputModal) SetHeight(height int) { - i.height = height +func (c *InputModal) SetHeight(height int) { + c.height = height } // SetMultiline returns whether this is a multiline modal -func (i *InputModal) SetMultiline(m bool) { - i.input.multiline = m +func (c *InputModal) SetMultiline(m bool) { + c.input.multiline = m } // IsMultiline returns whether this is a multiline modal -func (i *InputModal) IsMultiline() bool { - return i.input.multiline +func (c *InputModal) IsMultiline() bool { + return c.input.multiline } // IsBordered returns whether this control is bordered or not -func (i *InputModal) IsBordered() bool { - return i.bordered +func (c *InputModal) IsBordered() bool { + return c.bordered } // SetBordered sets whether we render a border around the frame -func (i *InputModal) SetBordered(b bool) { - i.bordered = b +func (c *InputModal) SetBordered(b bool) { + c.bordered = b } // IsTabSkipped returns whether this control has it's tabskip flag set -func (i *InputModal) IsTabSkipped() bool { - return i.tabSkip +func (c *InputModal) IsTabSkipped() bool { + return c.tabSkip } // SetTabSkip sets the tabskip flag for this control -func (i *InputModal) SetTabSkip(b bool) { - i.tabSkip = b +func (c *InputModal) SetTabSkip(b bool) { + c.tabSkip = b } // HelpIsShown Returns whether the modal is showing it's help text or not -func (i *InputModal) HelpIsShown() bool { return i.showHelp } +func (c *InputModal) HelpIsShown() bool { return c.showHelp } // ShowHelp Set the "Show Help" flag -func (i *InputModal) ShowHelp(b bool) { - i.showHelp = b +func (c *InputModal) ShowHelp(b bool) { + c.showHelp = b } // GetFgColor returns the foreground color -func (i *InputModal) GetFgColor() termbox.Attribute { return i.fg } +func (c *InputModal) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *InputModal) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *InputModal) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *InputModal) GetBgColor() termbox.Attribute { return i.bg } +func (c *InputModal) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *InputModal) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *InputModal) SetBgColor(bg termbox.Attribute) { + c.bg = bg } // Show Sets the visibility flag to true -func (i *InputModal) Show() { - i.isVisible = true +func (c *InputModal) Show() { + c.isVisible = true } // Hide Sets the visibility flag to false -func (i *InputModal) Hide() { - i.isVisible = false +func (c *InputModal) Hide() { + c.isVisible = false } // IsVisible returns the isVisible flag -func (i *InputModal) IsVisible() bool { - return i.isVisible +func (c *InputModal) IsVisible() bool { + return c.isVisible } // SetDone Sets the flag that tells whether this modal has completed it's purpose -func (i *InputModal) SetDone(b bool) { - i.isDone = b +func (c *InputModal) SetDone(b bool) { + c.isDone = b } // IsDone Returns the "isDone" flag -func (i *InputModal) IsDone() bool { - return i.isDone +func (c *InputModal) IsDone() bool { + return c.isDone } // IsAccepted Returns whether the modal has been accepted -func (i *InputModal) IsAccepted() bool { - return i.isAccepted +func (c *InputModal) IsAccepted() bool { + return c.isAccepted } // GetValue Return the current value of the input -func (i *InputModal) GetValue() string { return i.input.GetValue() } +func (c *InputModal) GetValue() string { return c.input.GetValue() } // SetValue Sets the value of the input to s -func (i *InputModal) SetValue(s string) { - i.input.SetValue(s) +func (c *InputModal) SetValue(s string) { + c.input.SetValue(s) } // SetInputWrap sets whether the input field will wrap long text or not -func (i *InputModal) SetInputWrap(b bool) { - i.input.SetWrap(b) +func (c *InputModal) SetInputWrap(b bool) { + c.input.SetWrap(b) } // Clear Resets all non-positional parameters of the modal -func (i *InputModal) Clear() { - i.title = "" - i.text = "" - i.input.SetValue("") - i.isDone = false - i.isVisible = false +func (c *InputModal) Clear() { + c.title = "" + c.text = "" + c.input.SetValue("") + c.isDone = false + c.isVisible = false } // HandleEvent Handle the termbox event, return true if it was consumed -func (i *InputModal) HandleEvent(event termbox.Event) bool { +func (c *InputModal) HandleEvent(event termbox.Event) bool { if event.Key == termbox.KeyEnter { - if !i.input.IsMultiline() || !i.inputSelected { + if !c.input.IsMultiline() || !c.inputSelected { // Done editing - i.isDone = true - i.isAccepted = true + c.isDone = true + c.isAccepted = true } else { - i.input.HandleEvent(event) + c.input.HandleEvent(event) } return true } else if event.Key == termbox.KeyTab { - if i.input.IsMultiline() { - i.inputSelected = !i.inputSelected + if c.input.IsMultiline() { + c.inputSelected = !c.inputSelected } } else if event.Key == termbox.KeyEsc { // Done editing - i.isDone = true - i.isAccepted = false + c.isDone = true + c.isAccepted = false return true } - return i.input.HandleEvent(event) + return c.input.HandleEvent(event) } // Draw Draw the modal -func (i *InputModal) Draw() { - if i.isVisible { +func (c *InputModal) Draw() { + if c.isVisible { // First blank out the area we'll be putting the modal - FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) - nextY := i.y + 1 + FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg) + nextY := c.y + 1 // The title - if i.title != "" { - if len(i.title) > i.width { - diff := i.width - len(i.title) - DrawStringAtPoint(i.title[:len(i.title)+diff-1], i.x+1, nextY, i.fg, i.bg) + if c.title != "" { + if len(c.title) > c.width { + diff := c.width - len(c.title) + DrawStringAtPoint(c.title[:len(c.title)+diff-1], c.x+1, nextY, c.fg, c.bg) } else { - DrawStringAtPoint(i.title, i.x+1, nextY, i.fg, i.bg) + DrawStringAtPoint(c.title, c.x+1, nextY, c.fg, c.bg) } nextY++ - FillWithChar('-', i.x+1, nextY, i.x+i.width-1, nextY, i.fg, i.bg) + FillWithChar('-', c.x+1, nextY, c.x+c.width-1, nextY, c.fg, c.bg) nextY++ } - if i.text != "" { - DrawStringAtPoint(i.text, i.x+1, nextY, i.fg, i.bg) + if c.text != "" { + DrawStringAtPoint(c.text, c.x+1, nextY, c.fg, c.bg) nextY++ } - i.input.SetY(nextY) - i.input.Draw() + c.input.SetY(nextY) + c.input.Draw() nextY += 3 - if i.showHelp { + if c.showHelp { helpString := " (ENTER) to Accept. (ESC) to Cancel. " - helpX := (i.x + i.width - len(helpString)) - 1 - DrawStringAtPoint(helpString, helpX, nextY, i.fg, i.bg) + helpX := (c.x + c.width - len(helpString)) - 1 + DrawStringAtPoint(helpString, helpX, nextY, c.fg, c.bg) } - if i.bordered { + if c.bordered { // Now draw the border - DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) + DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg) } } } diff --git a/termbox_label.go b/termbox_label.go index c67b897..57e368e 100644 --- a/termbox_label.go +++ b/termbox_label.go @@ -9,133 +9,133 @@ type Label struct { x, y, width, height int cursor int fg, bg termbox.Attribute + activeFg, activeBg termbox.Attribute bordered bool wrap bool multiline bool + active bool } // CreateLabel creates an input field at x, y that is w by h func CreateLabel(lbl string, x, y, w, h int, fg, bg termbox.Attribute) *Label { - i := Label{value: lbl, x: x, y: y, width: w, height: h, fg: fg, bg: bg} - return &i + c := Label{ + value: lbl, x: x, y: y, width: w, height: h, + fg: fg, bg: bg, activeFg: fg, activeBg: bg, + } + return &c } +func (c *Label) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg } +func (c *Label) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg } +func (c *Label) SetActive(a bool) { c.active = a } +func (c *Label) IsActive() bool { return c.active } + // IsTabSkipped is always true for a label -func (i *Label) IsTabSkipped() bool { - return true -} +func (c *Label) IsTabSkipped() bool { return true } // This doesn't do anything for a label -func (i *Label) SetTabSkip(b bool) {} +func (c *Label) SetTabSkip(b bool) {} // GetID returns this control's ID -func (i *Label) GetID() string { return i.id } +func (c *Label) GetID() string { return c.id } // SetID sets this control's ID -func (i *Label) SetID(newID string) { - i.id = newID -} +func (c *Label) SetID(newID string) { c.id = newID } // GetValue gets the current text that is in the Label -func (i *Label) GetValue() string { return i.value } +func (c *Label) GetValue() string { return c.value } // SetValue sets the current text in the Label to s -func (i *Label) SetValue(s string) { - i.value = s -} +func (c *Label) SetValue(s string) { c.value = s } // GetX returns the x position of the input field -func (i *Label) GetX() int { return i.x } +func (c *Label) GetX() int { return c.x } // SetX sets the x position of the input field -func (i *Label) SetX(x int) { - i.x = x -} +func (c *Label) SetX(x int) { c.x = x } // GetY returns the y position of the input field -func (i *Label) GetY() int { return i.y } +func (c *Label) GetY() int { return c.y } // SetY sets the y position of the input field -func (i *Label) SetY(y int) { - i.y = y -} +func (c *Label) SetY(y int) { c.y = y } // GetWidth returns the current width of the input field -func (i *Label) GetWidth() int { - if i.width == -1 { - if i.bordered { - return len(i.value) + 2 +func (c *Label) GetWidth() int { + if c.width == -1 { + if c.bordered { + return len(c.value) + 2 } - return len(i.value) + return len(c.value) } - return i.width + return c.width } // SetWidth sets the current width of the input field -func (i *Label) SetWidth(w int) { - i.width = w +func (c *Label) SetWidth(w int) { + c.width = w } // GetHeight returns the current height of the input field -func (i *Label) GetHeight() int { return i.height } +func (c *Label) GetHeight() int { return c.height } // SetHeight sets the current height of the input field -func (i *Label) SetHeight(h int) { - i.height = h +func (c *Label) SetHeight(h int) { + c.height = h } // GetFgColor returns the foreground color -func (i *Label) GetFgColor() termbox.Attribute { return i.fg } +func (c *Label) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *Label) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *Label) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *Label) GetBgColor() termbox.Attribute { return i.bg } +func (c *Label) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *Label) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *Label) SetBgColor(bg termbox.Attribute) { + c.bg = bg } // IsBordered returns true or false if this input field has a border -func (i *Label) IsBordered() bool { return i.bordered } +func (c *Label) IsBordered() bool { return c.bordered } // SetBordered sets whether we render a border around the input field -func (i *Label) SetBordered(b bool) { - i.bordered = b +func (c *Label) SetBordered(b bool) { + c.bordered = b } // DoesWrap returns true or false if this input field wraps text -func (i *Label) DoesWrap() bool { return i.wrap } +func (c *Label) DoesWrap() bool { return c.wrap } // SetWrap sets whether we wrap the text at width. -func (i *Label) SetWrap(b bool) { - i.wrap = b +func (c *Label) SetWrap(b bool) { + c.wrap = b } // IsMultiline returns true or false if this field can have multiple lines -func (i *Label) IsMultiline() bool { return i.multiline } +func (c *Label) IsMultiline() bool { return c.multiline } // SetMultiline sets whether the field can have multiple lines -func (i *Label) SetMultiline(b bool) { - i.multiline = b +func (c *Label) SetMultiline(b bool) { + c.multiline = b } // HandleEvent accepts the termbox event and returns whether it was consumed -func (i *Label) HandleEvent(event termbox.Event) bool { return false } +func (c *Label) HandleEvent(event termbox.Event) bool { return false } // Draw outputs the input field on the screen -func (i *Label) Draw() { - maxWidth := i.width - maxHeight := i.height - x, y := i.x, i.y - startX := i.x - startY := i.y - if i.bordered { - DrawBorder(i.x, i.y, i.x+i.GetWidth(), i.y+i.height, i.fg, i.bg) +func (c *Label) Draw() { + maxWidth := c.width + maxHeight := c.height + x, y := c.x, c.y + startX := c.x + startY := c.y + if c.bordered { + DrawBorder(c.x, c.y, c.x+c.GetWidth(), c.y+c.height, c.fg, c.bg) maxWidth-- maxHeight-- x++ @@ -144,5 +144,5 @@ func (i *Label) Draw() { startY++ } - DrawStringAtPoint(i.value, x, y, i.fg, i.bg) + DrawStringAtPoint(c.value, x, y, c.fg, c.bg) } diff --git a/termbox_menu.go b/termbox_menu.go index 700ab04..33bdd93 100644 --- a/termbox_menu.go +++ b/termbox_menu.go @@ -14,118 +14,134 @@ type Menu struct { bg, fg termbox.Attribute selectedBg, selectedFg termbox.Attribute disabledBg, disabledFg termbox.Attribute + activeFg, activeBg termbox.Attribute isDone bool bordered bool vimMode bool tabSkip bool + active bool } // CreateMenu Creates a menu with the specified attributes func CreateMenu(title string, options []string, x, y, width, height int, fg, bg termbox.Attribute) *Menu { - i := Menu{ + c := Menu{ title: title, x: x, y: y, width: width, height: height, fg: fg, bg: bg, selectedFg: bg, selectedBg: fg, disabledFg: bg, disabledBg: bg, + activeFg: fg, activeBg: bg, + bordered: true, } for _, line := range options { - i.options = append(i.options, MenuOption{text: line}) + c.options = append(c.options, MenuOption{text: line}) } - if len(i.options) > 0 { - i.SetSelectedOption(&i.options[0]) + if len(c.options) > 0 { + c.SetSelectedOption(&c.options[0]) } - return &i + return &c } +func (c *Menu) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg } + +func (c *Menu) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg } + +func (c *Menu) SetActive(a bool) { c.active = a } + +func (c *Menu) IsActive() bool { return c.active } + // GetID returns this control's ID -func (i *Menu) GetID() string { return i.id } +func (c *Menu) GetID() string { return c.id } // SetID sets this control's ID -func (i *Menu) SetID(newID string) { - i.id = newID -} +func (c *Menu) SetID(newID string) { c.id = newID } + +// IsTabSkipped is always true for a label +func (c *Menu) IsTabSkipped() bool { return true } + +// This doesn't do anything for a label +func (c *Menu) SetTabSkip(b bool) {} // GetTitle returns the current title of the menu -func (i *Menu) GetTitle() string { return i.title } +func (c *Menu) GetTitle() string { return c.title } // SetTitle sets the current title of the menu to s -func (i *Menu) SetTitle(s string) { - i.title = s +func (c *Menu) SetTitle(s string) { + c.title = s } // GetOptions returns the current options of the menu -func (i *Menu) GetOptions() []MenuOption { - return i.options +func (c *Menu) GetOptions() []MenuOption { + return c.options } // SetOptions set the menu's options to opts -func (i *Menu) SetOptions(opts []MenuOption) { - i.options = opts +func (c *Menu) SetOptions(opts []MenuOption) { + c.options = opts } // SetOptionsFromStrings sets the options of this menu from a slice of strings -func (i *Menu) SetOptionsFromStrings(opts []string) { +func (c *Menu) SetOptionsFromStrings(opts []string) { var newOpts []MenuOption for _, v := range opts { newOpts = append(newOpts, *CreateOptionFromText(v)) } - i.SetOptions(newOpts) - i.SetSelectedOption(i.GetOptionFromIndex(0)) + c.SetOptions(newOpts) + c.SetSelectedOption(c.GetOptionFromIndex(0)) } // GetX returns the current x coordinate of the menu -func (i *Menu) GetX() int { return i.x } +func (c *Menu) GetX() int { return c.x } // SetX sets the current x coordinate of the menu to x -func (i *Menu) SetX(x int) { - i.x = x +func (c *Menu) SetX(x int) { + c.x = x } // GetY returns the current y coordinate of the menu -func (i *Menu) GetY() int { return i.y } +func (c *Menu) GetY() int { return c.y } // SetY sets the current y coordinate of the menu to y -func (i *Menu) SetY(y int) { - i.y = y +func (c *Menu) SetY(y int) { + c.y = y } // GetWidth returns the current width of the menu -func (i *Menu) GetWidth() int { return i.width } +func (c *Menu) GetWidth() int { return c.width } // SetWidth sets the current menu width to width -func (i *Menu) SetWidth(width int) { - i.width = width +func (c *Menu) SetWidth(width int) { + c.width = width } // GetHeight returns the current height of the menu -func (i *Menu) GetHeight() int { return i.height } +func (c *Menu) GetHeight() int { return c.height } // SetHeight set the height of the menu to height -func (i *Menu) SetHeight(height int) { - i.height = height +func (c *Menu) SetHeight(height int) { + c.height = height } // GetSelectedOption returns the current selected option -func (i *Menu) GetSelectedOption() *MenuOption { - idx := i.GetSelectedIndex() +func (c *Menu) GetSelectedOption() *MenuOption { + idx := c.GetSelectedIndex() if idx != -1 { - return &i.options[idx] + return &c.options[idx] } return nil } // GetOptionFromIndex Returns the -func (i *Menu) GetOptionFromIndex(idx int) *MenuOption { - if idx >= 0 && idx < len(i.options) { - return &i.options[idx] +func (c *Menu) GetOptionFromIndex(idx int) *MenuOption { + if idx >= 0 && idx < len(c.options) { + return &c.options[idx] } return nil } // GetOptionFromText Returns the first option with the text v -func (i *Menu) GetOptionFromText(v string) *MenuOption { - for idx := range i.options { - testOption := &i.options[idx] +func (c *Menu) GetOptionFromText(v string) *MenuOption { + for idx := range c.options { + testOption := &c.options[idx] if testOption.GetText() == v { return testOption } @@ -135,9 +151,9 @@ func (i *Menu) GetOptionFromText(v string) *MenuOption { // GetSelectedIndex returns the index of the selected option // Returns -1 if nothing is selected -func (i *Menu) GetSelectedIndex() int { - for idx := range i.options { - if i.options[idx].IsSelected() { +func (c *Menu) GetSelectedIndex() int { + for idx := range c.options { + if c.options[idx].IsSelected() { return idx } } @@ -145,267 +161,288 @@ func (i *Menu) GetSelectedIndex() int { } // SetSelectedIndex sets the selection to setIdx -func (i *Menu) SetSelectedIndex(idx int) { - if len(i.options) > 0 { +func (c *Menu) SetSelectedIndex(idx int) { + if len(c.options) > 0 { if idx < 0 { idx = 0 - } else if idx >= len(i.options) { - idx = len(i.options) - 1 + } else if idx >= len(c.options) { + idx = len(c.options) - 1 } - i.SetSelectedOption(&i.options[idx]) + c.SetSelectedOption(&c.options[idx]) } } // SetSelectedOption sets the current selected option to v (if it's valid) -func (i *Menu) SetSelectedOption(v *MenuOption) { - for idx := range i.options { - if &i.options[idx] == v { - i.options[idx].Select() +func (c *Menu) SetSelectedOption(v *MenuOption) { + for idx := range c.options { + if &c.options[idx] == v { + c.options[idx].Select() } else { - i.options[idx].Unselect() + c.options[idx].Unselect() } } } // SelectPrevOption Decrements the selected option (if it can) -func (i *Menu) SelectPrevOption() { - idx := i.GetSelectedIndex() +func (c *Menu) SelectPrevOption() { + idx := c.GetSelectedIndex() for idx >= 0 { idx-- - testOption := i.GetOptionFromIndex(idx) + testOption := c.GetOptionFromIndex(idx) if testOption != nil && !testOption.IsDisabled() { - i.SetSelectedOption(testOption) + c.SetSelectedOption(testOption) return } } } // SelectNextOption Increments the selected option (if it can) -func (i *Menu) SelectNextOption() { - idx := i.GetSelectedIndex() - for idx < len(i.options) { +func (c *Menu) SelectNextOption() { + idx := c.GetSelectedIndex() + for idx < len(c.options) { idx++ - testOption := i.GetOptionFromIndex(idx) + testOption := c.GetOptionFromIndex(idx) if testOption != nil && !testOption.IsDisabled() { - i.SetSelectedOption(testOption) + c.SetSelectedOption(testOption) return } } } // SelectPageUpOption Goes up 'menu height' options -func (i *Menu) SelectPageUpOption() { - idx := i.GetSelectedIndex() - idx -= i.height +func (c *Menu) SelectPageUpOption() { + idx := c.GetSelectedIndex() + idx -= c.height if idx < 0 { idx = 0 } - i.SetSelectedIndex(idx) + c.SetSelectedIndex(idx) return } // SelectPageDownOption Goes down 'menu height' options -func (i *Menu) SelectPageDownOption() { - idx := i.GetSelectedIndex() - idx += i.height - if idx >= len(i.options) { - idx = len(i.options) - 1 +func (c *Menu) SelectPageDownOption() { + idx := c.GetSelectedIndex() + idx += c.height + if idx >= len(c.options) { + idx = len(c.options) - 1 } - i.SetSelectedIndex(idx) + c.SetSelectedIndex(idx) return } // SelectFirstOption Goes to the top -func (i *Menu) SelectFirstOption() { - i.SetSelectedIndex(0) +func (c *Menu) SelectFirstOption() { + c.SetSelectedIndex(0) return } // SelectLastOption Goes to the bottom -func (i *Menu) SelectLastOption() { - i.SetSelectedIndex(len(i.options) - 1) +func (c *Menu) SelectLastOption() { + c.SetSelectedIndex(len(c.options) - 1) return } // SetOptionDisabled Disables the specified option -func (i *Menu) SetOptionDisabled(idx int) { - if len(i.options) > idx { - i.GetOptionFromIndex(idx).Disable() +func (c *Menu) SetOptionDisabled(idx int) { + if len(c.options) > idx { + c.GetOptionFromIndex(idx).Disable() } } // SetOptionEnabled Enables the specified option -func (i *Menu) SetOptionEnabled(idx int) { - if len(i.options) > idx { - i.GetOptionFromIndex(idx).Enable() +func (c *Menu) SetOptionEnabled(idx int) { + if len(c.options) > idx { + c.GetOptionFromIndex(idx).Enable() } } // HelpIsShown returns true or false if the help is displayed -func (i *Menu) HelpIsShown() bool { return i.showHelp } +func (c *Menu) HelpIsShown() bool { return c.showHelp } // ShowHelp sets whether or not to display the help text -func (i *Menu) ShowHelp(b bool) { - i.showHelp = b +func (c *Menu) ShowHelp(b bool) { + c.showHelp = b } // GetFgColor returns the foreground color -func (i *Menu) GetFgColor() termbox.Attribute { return i.fg } +func (c *Menu) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *Menu) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *Menu) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *Menu) GetBgColor() termbox.Attribute { return i.bg } +func (c *Menu) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *Menu) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *Menu) SetBgColor(bg termbox.Attribute) { + c.bg = bg } // IsDone returns whether the user has answered the modal -func (i *Menu) IsDone() bool { return i.isDone } +func (c *Menu) IsDone() bool { return c.isDone } // SetDone sets whether the modal has completed it's purpose -func (i *Menu) SetDone(b bool) { - i.isDone = b +func (c *Menu) SetDone(b bool) { + c.isDone = b } // IsBordered returns true or false if this menu has a border -func (i *Menu) IsBordered() bool { return i.bordered } +func (c *Menu) IsBordered() bool { return c.bordered } // SetBordered sets whether we render a border around the menu -func (i *Menu) SetBordered(b bool) { - i.bordered = b +func (c *Menu) SetBordered(b bool) { + c.bordered = b } // EnableVimMode Enables h,j,k,l navigation -func (i *Menu) EnableVimMode() { - i.vimMode = true +func (c *Menu) EnableVimMode() { + c.vimMode = true } // DisableVimMode Disables h,j,k,l navigation -func (i *Menu) DisableVimMode() { - i.vimMode = false +func (c *Menu) DisableVimMode() { + c.vimMode = false } // HandleEvent handles the termbox event and returns whether it was consumed -func (i *Menu) HandleEvent(event termbox.Event) bool { +func (c *Menu) HandleEvent(event termbox.Event) bool { if event.Key == termbox.KeyEnter || event.Key == termbox.KeySpace { - i.isDone = true + c.isDone = true return true } - currentIdx := i.GetSelectedIndex() + currentIdx := c.GetSelectedIndex() switch event.Key { case termbox.KeyArrowUp: - i.SelectPrevOption() + c.SelectPrevOption() case termbox.KeyArrowDown: - i.SelectNextOption() + c.SelectNextOption() case termbox.KeyArrowLeft: - i.SelectPageUpOption() + c.SelectPageUpOption() case termbox.KeyArrowRight: - i.SelectPageDownOption() + c.SelectPageDownOption() } - if i.vimMode { + if c.vimMode { switch event.Ch { case 'j': - i.SelectNextOption() + c.SelectNextOption() case 'k': - i.SelectPrevOption() + c.SelectPrevOption() } if event.Key == termbox.KeyCtrlF { - i.SelectPageDownOption() + c.SelectPageDownOption() } else if event.Key == termbox.KeyCtrlB { - i.SelectPageUpOption() + c.SelectPageUpOption() } } - if i.GetSelectedIndex() != currentIdx { + if c.GetSelectedIndex() != currentIdx { return true } return false } // Draw draws the modal -func (i *Menu) Draw() { - // First blank out the area we'll be putting the menu - FillWithChar(' ', i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) - // Now draw the border - optionStartX := i.x - optionStartY := i.y - optionWidth := i.width - optionHeight := i.height - if optionHeight == -1 { - optionHeight = len(i.options) +func (c *Menu) Draw() { + useFg, useBg := c.fg, c.bg + if c.active { + useFg, useBg = c.activeFg, c.activeBg } - if i.bordered { - if i.height == -1 { - DrawBorder(i.x, i.y, i.x+i.width, i.y+optionHeight+1, i.fg, i.bg) + // First blank out the area we'll be putting the menu + FillWithChar(' ', c.x, c.y, c.x+c.width, c.y+c.height, useFg, useBg) + // Now draw the border + optionStartX := c.x + optionStartY := c.y + optionWidth := c.width + _ = optionWidth + optionHeight := c.height + if optionHeight == -1 { + optionHeight = len(c.options) + } + if c.bordered { + pct := float64(c.GetSelectedIndex()) / float64(len(c.options)) + if c.title == "" { + if len(c.options) > c.height-2 { + DrawBorderWithPct(c.x, c.y, c.x+c.width, c.y+c.height, pct, useFg, useBg) + } else { + DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, useFg, useBg) + } } else { - DrawBorder(i.x, i.y, i.x+i.width, i.y+optionHeight, i.fg, i.bg) + if len(c.options) > c.height-2 { + DrawBorderWithTitleAndPct(c.x, c.y, c.x+c.width, c.y+c.height, " "+c.title+" ", pct, useFg, useBg) + } else { + DrawBorderWithTitle(c.x, c.y, c.x+c.width, c.y+c.height, " "+c.title+" ", useFg, useBg) + } } - optionStartX = i.x + 1 - optionStartY = i.y + 1 - optionWidth = i.width - 1 + optionStartX = c.x + 1 + optionStartY = c.y + 1 + optionWidth = c.width - 1 optionHeight -= 2 } - // The title - if i.title != "" { - DrawStringAtPoint(AlignText(i.title, optionWidth, AlignCenter), optionStartX, optionStartY, i.fg, i.bg) - optionStartY++ - if i.bordered { - FillWithChar('-', optionStartX, optionStartY, optionWidth, optionStartY, i.fg, i.bg) - optionStartY++ - optionHeight-- + if len(c.options) > 0 { + firstDispIdx := 0 + lastDispIdx := len(c.options) - 1 + if len(c.options) > c.height-2 { + lastDispIdx = c.height - 2 } - optionHeight-- - } - - if len(i.options) > 0 { - // If the currently selected option is disabled, move to the next - if i.GetSelectedOption().IsDisabled() { - i.SelectNextOption() + if c.GetSelectedIndex() > c.height-2 { + firstDispIdx = c.GetSelectedIndex() - (c.height - 2) + lastDispIdx = c.GetSelectedIndex() } - - // Print the options - bldHeight := (optionHeight / 2) - startIdx := i.GetSelectedIndex() - endIdx := i.GetSelectedIndex() - for bldHeight > 0 && startIdx >= 1 { - startIdx-- - bldHeight-- - } - bldHeight += (optionHeight / 2) - for bldHeight > 0 && endIdx < len(i.options) { - endIdx++ - bldHeight-- - } - - for idx := startIdx; idx < endIdx; idx++ { //i.options { - if i.GetSelectedIndex()-idx >= optionHeight-1 { - // Skip this one - continue - } - currOpt := &i.options[idx] + for idx := firstDispIdx; idx < lastDispIdx; idx++ { + currOpt := &c.options[idx] outTxt := currOpt.GetText() - if len(outTxt) >= i.width { - outTxt = outTxt[:i.width] - } if currOpt.IsDisabled() { - DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.disabledFg, i.disabledBg) - } else if i.GetSelectedOption() == currOpt { - DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.selectedFg, i.selectedBg) + DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.disabledFg, c.disabledBg) + } else if c.GetSelectedOption() == currOpt { + DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.selectedFg, c.selectedBg) } else { - DrawStringAtPoint(outTxt, optionStartX, optionStartY, i.fg, i.bg) + DrawStringAtPoint(outTxt, optionStartX, optionStartY, useFg, useBg) } optionStartY++ - if optionStartY > i.y+optionHeight-1 { - break - } } } + /* + // Print the options + bldHeight := (optionHeight / 2) + startIdx := c.GetSelectedIndex() + endIdx := c.GetSelectedIndex() + for bldHeight > 0 && startIdx >= 1 { + startIdx-- + bldHeight-- + } + bldHeight += (optionHeight / 2) + for bldHeight > 0 && endIdx < len(c.options) { + endIdx++ + bldHeight-- + } + + for idx := startIdx; idx < endIdx; idx++ { + if c.GetSelectedIndex()-idx >= optionHeight-1 { + // Skip this one + continue + } + currOpt := &c.options[idx] + outTxt := currOpt.GetText() + if len(outTxt) >= c.width { + outTxt = outTxt[:c.width] + } + if currOpt.IsDisabled() { + DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.disabledFg, c.disabledBg) + } else if c.GetSelectedOption() == currOpt { + DrawStringAtPoint(outTxt, optionStartX, optionStartY, c.selectedFg, c.selectedBg) + } else { + DrawStringAtPoint(outTxt, optionStartX, optionStartY, useFg, useBg) + } + optionStartY++ + if optionStartY > c.y+optionHeight-1 { + break + } + } + } + */ } /* MenuOption Struct & methods */ @@ -427,52 +464,52 @@ func CreateOptionFromText(s string) *MenuOption { } // SetText Sets the text for this option -func (i *MenuOption) SetText(s string) { - i.text = s +func (c *MenuOption) SetText(s string) { + c.text = s } // GetText Returns the text for this option -func (i *MenuOption) GetText() string { return i.text } +func (c *MenuOption) GetText() string { return c.text } // Disable Sets this option to disabled -func (i *MenuOption) Disable() { - i.disabled = true +func (c *MenuOption) Disable() { + c.disabled = true } // Enable Sets this option to enabled -func (i *MenuOption) Enable() { - i.disabled = false +func (c *MenuOption) Enable() { + c.disabled = false } // IsDisabled returns whether this option is enabled -func (i *MenuOption) IsDisabled() bool { - return i.disabled +func (c *MenuOption) IsDisabled() bool { + return c.disabled } // IsSelected Returns whether this option is selected -func (i *MenuOption) IsSelected() bool { - return i.selected +func (c *MenuOption) IsSelected() bool { + return c.selected } // Select Sets this option to selected -func (i *MenuOption) Select() { - i.selected = true +func (c *MenuOption) Select() { + c.selected = true } // Unselect Sets this option to not selected -func (i *MenuOption) Unselect() { - i.selected = false +func (c *MenuOption) Unselect() { + c.selected = false } // SetHelpText Sets this option's help text to s -func (i *MenuOption) SetHelpText(s string) { - i.helpText = s +func (c *MenuOption) SetHelpText(s string) { + c.helpText = s } // GetHelpText Returns the help text for this option -func (i *MenuOption) GetHelpText() string { return i.helpText } +func (c *MenuOption) GetHelpText() string { return c.helpText } // AddToSubMenu adds a slice of MenuOptions to this option -func (i *MenuOption) AddToSubMenu(sub *MenuOption) { - i.subMenu = append(i.subMenu, *sub) +func (c *MenuOption) AddToSubMenu(sub *MenuOption) { + c.subMenu = append(c.subMenu, *sub) } diff --git a/termbox_progressbar.go b/termbox_progressbar.go index 962433e..21e1330 100644 --- a/termbox_progressbar.go +++ b/termbox_progressbar.go @@ -15,157 +15,165 @@ type ProgressBar struct { alignment TextAlignment colorized bool - x, y int - width, height int - bg, fg termbox.Attribute + x, y int + width, height int + fg, bg termbox.Attribute + activeFg, activeBg termbox.Attribute + active bool } // CreateProgressBar Create a progress bar object func CreateProgressBar(tot, x, y int, fg, bg termbox.Attribute) *ProgressBar { - i := ProgressBar{total: tot, + c := ProgressBar{total: tot, fullChar: '#', emptyChar: ' ', x: x, y: y, height: 1, width: 10, bordered: true, fg: fg, bg: bg, + activeFg: fg, activeBg: bg, alignment: AlignLeft, } - return &i + return &c } +func (c *ProgressBar) SetActiveFgColor(fg termbox.Attribute) { c.activeFg = fg } +func (c *ProgressBar) SetActiveBgColor(bg termbox.Attribute) { c.activeBg = bg } +func (c *ProgressBar) SetActive(a bool) { c.active = a } +func (c *ProgressBar) IsActive() bool { return c.active } + // GetID returns this control's ID -func (i *ProgressBar) GetID() string { return i.id } +func (c *ProgressBar) GetID() string { return c.id } // SetID sets this control's ID -func (i *ProgressBar) SetID(newID string) { - i.id = newID +func (c *ProgressBar) SetID(newID string) { + c.id = newID } // GetProgress returns the curret progress value -func (i *ProgressBar) GetProgress() int { - return i.progress +func (c *ProgressBar) GetProgress() int { + return c.progress } // SetProgress sets the current progress of the bar -func (i *ProgressBar) SetProgress(p int) { - if (p <= i.total || i.allowOverflow) || (p >= 0 || i.allowUnderflow) { - i.progress = p +func (c *ProgressBar) SetProgress(p int) { + if (p <= c.total || c.allowOverflow) || (p >= 0 || c.allowUnderflow) { + c.progress = p } } // IncrProgress increments the current progress of the bar -func (i *ProgressBar) IncrProgress() { - if i.progress < i.total || i.allowOverflow { - i.progress++ +func (c *ProgressBar) IncrProgress() { + if c.progress < c.total || c.allowOverflow { + c.progress++ } } // DecrProgress decrements the current progress of the bar -func (i *ProgressBar) DecrProgress() { - if i.progress > 0 || i.allowUnderflow { - i.progress-- +func (c *ProgressBar) DecrProgress() { + if c.progress > 0 || c.allowUnderflow { + c.progress-- } } // GetPercent returns the percent full of the bar -func (i *ProgressBar) GetPercent() int { - return int(float64(i.progress) / float64(i.total) * 100) +func (c *ProgressBar) GetPercent() int { + return int(float64(c.progress) / float64(c.total) * 100) } // EnableOverflow Tells the progress bar that it can go over the total -func (i *ProgressBar) EnableOverflow() { - i.allowOverflow = true +func (c *ProgressBar) EnableOverflow() { + c.allowOverflow = true } // DisableOverflow Tells the progress bar that it can NOT go over the total -func (i *ProgressBar) DisableOverflow() { - i.allowOverflow = false +func (c *ProgressBar) DisableOverflow() { + c.allowOverflow = false } // EnableUnderflow Tells the progress bar that it can go below zero -func (i *ProgressBar) EnableUnderflow() { - i.allowUnderflow = true +func (c *ProgressBar) EnableUnderflow() { + c.allowUnderflow = true } // DisableUnderflow Tells the progress bar that it can NOT go below zero -func (i *ProgressBar) DisableUnderflow() { - i.allowUnderflow = false +func (c *ProgressBar) DisableUnderflow() { + c.allowUnderflow = false } // GetFullChar returns the rune used for 'full' -func (i *ProgressBar) GetFullChar() rune { - return i.fullChar +func (c *ProgressBar) GetFullChar() rune { + return c.fullChar } // SetFullChar sets the rune used for 'full' -func (i *ProgressBar) SetFullChar(f rune) { - i.fullChar = f +func (c *ProgressBar) SetFullChar(f rune) { + c.fullChar = f } // GetEmptyChar gets the rune used for 'empty' -func (i *ProgressBar) GetEmptyChar() rune { - return i.emptyChar +func (c *ProgressBar) GetEmptyChar() rune { + return c.emptyChar } // SetEmptyChar sets the rune used for 'empty' -func (i *ProgressBar) SetEmptyChar(f rune) { - i.emptyChar = f +func (c *ProgressBar) SetEmptyChar(f rune) { + c.emptyChar = f } // GetX Return the x position of the Progress Bar -func (i *ProgressBar) GetX() int { return i.x } +func (c *ProgressBar) GetX() int { return c.x } // SetX set the x position of the ProgressBar to x -func (i *ProgressBar) SetX(x int) { - i.x = x +func (c *ProgressBar) SetX(x int) { + c.x = x } // GetY Return the y position of the ProgressBar -func (i *ProgressBar) GetY() int { return i.y } +func (c *ProgressBar) GetY() int { return c.y } // SetY Set the y position of the ProgressBar to y -func (i *ProgressBar) SetY(y int) { - i.y = y +func (c *ProgressBar) SetY(y int) { + c.y = y } // GetHeight returns the height of the progress bar // Defaults to 1 (3 if bordered) -func (i *ProgressBar) GetHeight() int { - return i.height +func (c *ProgressBar) GetHeight() int { + return c.height } // SetHeight Sets the height of the progress bar -func (i *ProgressBar) SetHeight(h int) { - i.height = h +func (c *ProgressBar) SetHeight(h int) { + c.height = h } // GetWidth returns the width of the progress bar -func (i *ProgressBar) GetWidth() int { - return i.width +func (c *ProgressBar) GetWidth() int { + return c.width } // SetWidth Sets the width of the progress bar -func (i *ProgressBar) SetWidth(w int) { - i.width = w +func (c *ProgressBar) SetWidth(w int) { + c.width = w } // GetFgColor returns the foreground color -func (i *ProgressBar) GetFgColor() termbox.Attribute { return i.fg } +func (c *ProgressBar) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *ProgressBar) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *ProgressBar) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *ProgressBar) GetBgColor() termbox.Attribute { return i.bg } +func (c *ProgressBar) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *ProgressBar) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *ProgressBar) SetBgColor(bg termbox.Attribute) { + c.bg = bg } // Align Tells which direction the progress bar empties -func (i *ProgressBar) Align(a TextAlignment) { - i.alignment = a +func (c *ProgressBar) Align(a TextAlignment) { + c.alignment = a } // SetColorized sets whether the progress bar should be colored @@ -173,48 +181,48 @@ func (i *ProgressBar) Align(a TextAlignment) { // 10% - Red // 50% - Yellow // 80% - Green -func (i *ProgressBar) SetColorized(c bool) { - i.colorized = c +func (c *ProgressBar) SetColorized(color bool) { + c.colorized = color } // HandleEvent accepts the termbox event and returns whether it was consumed -func (i *ProgressBar) HandleEvent(event termbox.Event) bool { +func (c *ProgressBar) HandleEvent(event termbox.Event) bool { return false } // Draw outputs the input field on the screen -func (i *ProgressBar) Draw() { +func (c *ProgressBar) Draw() { // For now, just draw a [#### ] bar // TODO: make this more advanced - useFg := i.fg - if i.colorized { - if i.GetPercent() < 10 { + useFg := c.fg + if c.colorized { + if c.GetPercent() < 10 { useFg = termbox.ColorRed - } else if i.GetPercent() < 50 { + } else if c.GetPercent() < 50 { useFg = termbox.ColorYellow } else { useFg = termbox.ColorGreen } } - drawX, drawY := i.x, i.y - fillWidth, fillHeight := i.width-2, i.height - DrawStringAtPoint("[", drawX, drawY, i.fg, i.bg) - numFull := int(float64(fillWidth) * float64(i.progress) / float64(i.total)) - FillWithChar(i.fullChar, drawX+1, drawY, drawX+1+numFull, drawY+(fillHeight-1), useFg, i.bg) - DrawStringAtPoint("]", drawX+i.width-1, drawY, i.fg, i.bg) + drawX, drawY := c.x, c.y + fillWidth, fillHeight := c.width-2, c.height + DrawStringAtPoint("[", drawX, drawY, c.fg, c.bg) + numFull := int(float64(fillWidth) * float64(c.progress) / float64(c.total)) + FillWithChar(c.fullChar, drawX+1, drawY, drawX+1+numFull, drawY+(fillHeight-1), useFg, c.bg) + DrawStringAtPoint("]", drawX+c.width-1, drawY, c.fg, c.bg) /* - drawX, drawY := i.x, i.y - drawWidth, drawHeight := i.width, i.height - if i.bordered { - if i.height == 1 && i.width > 2 { + drawX, drawY := c.x, c.y + drawWidth, drawHeight := c.width, c.height + if c.bordered { + if c.height == 1 && c.width > 2 { // Just using [ & ] for the border - DrawStringAtPoint("[", drawX, drawY, i.fg, i.bg) - DrawStringAtPoint("]", drawX+i.width-1, drawY, i.fg, i.bg) + DrawStringAtPoint("[", drawX, drawY, c.fg, c.bg) + DrawStringAtPoint("]", drawX+c.width-1, drawY, c.fg, c.bg) drawX++ drawWidth -= 2 - } else if i.height >= 3 { - DrawBorder(drawX, drawY, drawX+i.width, drawY+i.height, i.fg, i.bg) + } else if c.height >= 3 { + DrawBorder(drawX, drawY, drawX+c.width, drawY+c.height, c.fg, c.bg) drawX++ drawY++ drawWidth -= 2 @@ -223,14 +231,14 @@ func (i *ProgressBar) Draw() { } // Figure out how many chars are full - numFull := drawWidth * (i.progress / i.total) - switch i.alignment { + numFull := drawWidth * (c.progress / c.total) + switch c.alignment { case AlignRight: // TODO: Fill from right to left case AlignCenter: // TODO: Fill from middle out default: // Fill from left to right - FillWithChar(i.fullChar, drawX, drawY, drawX+numFull, drawY+(drawHeight-1), i.fg, i.bg) + FillWithChar(c.fullChar, drawX, drawY, drawX+numFull, drawY+(drawHeight-1), c.fg, c.bg) if numFull < drawWidth { - FillWithChar(i.emptyChar, drawX+numFull, drawY, drawX+drawWidth-1, drawY+(drawHeight-1), i.fg, i.bg) + FillWithChar(c.emptyChar, drawX+numFull, drawY, drawX+drawWidth-1, drawY+(drawHeight-1), c.fg, c.bg) } } */ diff --git a/termbox_scrollframe.go b/termbox_scrollframe.go index 0b83de4..74f5aba 100644 --- a/termbox_scrollframe.go +++ b/termbox_scrollframe.go @@ -10,126 +10,131 @@ type ScrollFrame struct { scrollX, scrollY int tabIdx int fg, bg termbox.Attribute + activeFg, activeBg termbox.Attribute bordered bool controls []termboxControl + active bool } // CreateScrollFrame creates Scrolling Frame at x, y that is w by h func CreateScrollFrame(x, y, w, h int, fg, bg termbox.Attribute) *ScrollFrame { - s := ScrollFrame{x: x, y: y, width: w, height: h, fg: fg, bg: bg} - return &s + c := ScrollFrame{ + x: x, y: y, width: w, height: h, + fg: fg, bg: bg, activeFg: fg, activeBg: bg, + } + return &c } // GetID returns this control's ID -func (i *ScrollFrame) GetID() string { return i.id } +func (c *ScrollFrame) GetID() string { return c.id } // SetID sets this control's ID -func (i *ScrollFrame) SetID(newID string) { - i.id = newID +func (c *ScrollFrame) SetID(newID string) { + c.id = newID } // GetX returns the x position of the scroll frame -func (i *ScrollFrame) GetX() int { return i.x } +func (c *ScrollFrame) GetX() int { return c.x } // SetX sets the x position of the scroll frame -func (i *ScrollFrame) SetX(x int) { - i.x = x +func (c *ScrollFrame) SetX(x int) { + c.x = x } // GetY returns the y position of the scroll frame -func (i *ScrollFrame) GetY() int { return i.y } +func (c *ScrollFrame) GetY() int { return c.y } // SetY sets the y position of the scroll frame -func (i *ScrollFrame) SetY(y int) { - i.y = y +func (c *ScrollFrame) SetY(y int) { + c.y = y } // GetWidth returns the current width of the scroll frame -func (i *ScrollFrame) GetWidth() int { return i.width } +func (c *ScrollFrame) GetWidth() int { return c.width } // SetWidth sets the current width of the scroll frame -func (i *ScrollFrame) SetWidth(w int) { - i.width = w +func (c *ScrollFrame) SetWidth(w int) { + c.width = w } // GetHeight returns the current height of the scroll frame -func (i *ScrollFrame) GetHeight() int { return i.height } +func (c *ScrollFrame) GetHeight() int { return c.height } // SetHeight sets the current height of the scroll frame -func (i *ScrollFrame) SetHeight(h int) { - i.height = h +func (c *ScrollFrame) SetHeight(h int) { + c.height = h } // GetFgColor returns the foreground color -func (i *ScrollFrame) GetFgColor() termbox.Attribute { return i.fg } +func (c *ScrollFrame) GetFgColor() termbox.Attribute { return c.fg } // SetFgColor sets the foreground color -func (i *ScrollFrame) SetFgColor(fg termbox.Attribute) { - i.fg = fg +func (c *ScrollFrame) SetFgColor(fg termbox.Attribute) { + c.fg = fg } // GetBgColor returns the background color -func (i *ScrollFrame) GetBgColor() termbox.Attribute { return i.bg } +func (c *ScrollFrame) GetBgColor() termbox.Attribute { return c.bg } // SetBgColor sets the current background color -func (i *ScrollFrame) SetBgColor(bg termbox.Attribute) { - i.bg = bg +func (c *ScrollFrame) SetBgColor(bg termbox.Attribute) { + c.bg = bg } // IsBordered returns true or false if this scroll frame has a border -func (i *ScrollFrame) IsBordered() bool { return i.bordered } +func (c *ScrollFrame) IsBordered() bool { return c.bordered } // SetBordered sets whether we render a border around the scroll frame -func (i *ScrollFrame) SetBordered(b bool) { - i.bordered = b +func (c *ScrollFrame) SetBordered(b bool) { + c.bordered = b } // GetScrollX returns the x distance scrolled -func (i *ScrollFrame) GetScrollX() int { - return i.scrollX +func (c *ScrollFrame) GetScrollX() int { + return c.scrollX } // GetScrollY returns the y distance scrolled -func (i *ScrollFrame) GetScrollY() int { - return i.scrollY +func (c *ScrollFrame) GetScrollY() int { + return c.scrollY } // ScrollDown scrolls the frame down -func (i *ScrollFrame) ScrollDown() { - i.scrollY++ +func (c *ScrollFrame) ScrollDown() { + c.scrollY++ } // ScrollUp scrolls the frame up -func (i *ScrollFrame) ScrollUp() { - if i.scrollY > 0 { - i.scrollY-- +func (c *ScrollFrame) ScrollUp() { + if c.scrollY > 0 { + c.scrollY-- } } // ScrollLeft scrolls the frame left -func (i *ScrollFrame) ScrollLeft() { - if i.scrollX > 0 { - i.scrollX-- +func (c *ScrollFrame) ScrollLeft() { + if c.scrollX > 0 { + c.scrollX-- } } // ScrollRight scrolls the frame right -func (i *ScrollFrame) ScrollRight() { - i.scrollX++ +func (c *ScrollFrame) ScrollRight() { + c.scrollX++ } // AddControl adds a control to the frame -func (i *ScrollFrame) AddControl(t termboxControl) { - i.controls = append(i.controls, t) +func (c *ScrollFrame) AddControl(t termboxControl) { + c.controls = append(c.controls, t) } // DrawControl figures out the relative position of the control, // sets it, draws it, then resets it. -func (i *ScrollFrame) DrawControl(t termboxControl) { - if i.IsVisible(t) { +func (c *ScrollFrame) DrawControl(t termboxControl) { + if c.IsVisible(t) { ctlX, ctlY := t.GetX(), t.GetY() - t.SetX((i.GetX() + ctlX)) - t.SetY((i.GetY() + ctlY)) + t.SetX((c.GetX() + ctlX)) + t.SetY((c.GetY() + ctlY)) t.Draw() t.SetX(ctlX) t.SetY(ctlY) @@ -138,35 +143,35 @@ func (i *ScrollFrame) DrawControl(t termboxControl) { // IsVisible takes a Termbox Control and returns whether // that control would be visible in the frame -func (i *ScrollFrame) IsVisible(t termboxControl) bool { +func (c *ScrollFrame) IsVisible(t termboxControl) bool { // Check if any part of t should be visible cX, cY := t.GetX(), t.GetY() - if cX+t.GetWidth() >= i.scrollX && cX <= i.scrollX+i.width { - return cY+t.GetHeight() >= i.scrollY && cY <= i.scrollY+i.height + if cX+t.GetWidth() >= c.scrollX && cX <= c.scrollX+c.width { + return cY+t.GetHeight() >= c.scrollY && cY <= c.scrollY+c.height } return false } // HandleEvent accepts the termbox event and returns whether it was consumed -func (i *ScrollFrame) HandleEvent(event termbox.Event) bool { +func (c *ScrollFrame) HandleEvent(event termbox.Event) bool { return false } // DrawToStrings generates a slice of strings with what should // be drawn to the screen -func (i *ScrollFrame) DrawToStrings() []string { +func (c *ScrollFrame) DrawToStrings() []string { return []string{} } // Draw outputs the Scoll Frame on the screen -func (i *ScrollFrame) Draw() { - maxWidth := i.width - maxHeight := i.height - x, y := i.x, i.y - startX := i.x - startY := i.y - if i.bordered { - DrawBorder(i.x, i.y, i.x+i.width, i.y+i.height, i.fg, i.bg) +func (c *ScrollFrame) Draw() { + maxWidth := c.width + maxHeight := c.height + x, y := c.x, c.y + startX := c.x + startY := c.y + if c.bordered { + DrawBorder(c.x, c.y, c.x+c.width, c.y+c.height, c.fg, c.bg) maxWidth-- maxHeight-- x++ @@ -174,7 +179,7 @@ func (i *ScrollFrame) Draw() { startX++ startY++ } - for idx := range i.controls { - i.DrawControl(i.controls[idx]) + for idx := range c.controls { + c.DrawControl(c.controls[idx]) } } diff --git a/termbox_util.go b/termbox_util.go index fdab4ba..da662cf 100644 --- a/termbox_util.go +++ b/termbox_util.go @@ -1,6 +1,7 @@ package termboxUtil import ( + "errors" "fmt" "strings" @@ -27,6 +28,10 @@ type termboxControl interface { SetTabSkip(bool) IsTabSkipped() bool Draw() + SetActive(bool) + IsActive() bool + SetActiveFgColor(termbox.Attribute) + SetActiveBgColor(termbox.Attribute) } // TextAlignment is an int value for how we're aligning text @@ -106,17 +111,67 @@ func FillWithChar(r rune, x1, y1, x2, y2 int, fg termbox.Attribute, bg termbox.A } // DrawBorder Draw a border around the area inside x1,y1 -> x2, y2 -func DrawBorder(x1, y1, x2, y2 int, fg termbox.Attribute, bg termbox.Attribute) { - termbox.SetCell(x1, y1, '+', fg, bg) - FillWithChar('-', x1+1, y1, x2-1, y1, fg, bg) - termbox.SetCell(x2, y1, '+', fg, bg) +func DrawBorder(x1, y1, x2, y2 int, fg, bg termbox.Attribute) { + termbox.SetCell(x1, y1, '╔', fg, bg) + FillWithChar('═', x1+1, y1, x2-1, y1, fg, bg) + termbox.SetCell(x2, y1, '╗', fg, bg) - FillWithChar('|', x1, y1+1, x1, y2-1, fg, bg) - FillWithChar('|', x2, y1+1, x2, y2-1, fg, bg) + FillWithChar('║', x1, y1+1, x1, y2-1, fg, bg) + FillWithChar('║', x2, y1+1, x2, y2-1, fg, bg) - termbox.SetCell(x1, y2, '+', fg, bg) - FillWithChar('-', x1+1, y2, x2-1, y2, fg, bg) - termbox.SetCell(x2, y2, '+', fg, bg) + termbox.SetCell(x1, y2, '╚', fg, bg) + FillWithChar('═', x1+1, y2, x2-1, y2, fg, bg) + termbox.SetCell(x2, y2, '╝', fg, bg) +} + +func DrawBorderWithPct(x1, y1, x2, y2 int, pct float64, fg, bg termbox.Attribute) { + termbox.SetCell(x1, y1, '╔', fg, bg) + + FillWithChar('═', x1+1, y1, x2-1, y1, fg, bg) + termbox.SetCell(x2, y1, '╗', fg, bg) + + FillWithChar('║', x1, y1+1, x1, y2-1, fg, bg) + FillWithChar('║', x2, y1+1, x2, y2-1, fg, bg) + // Now the percent indicator + pctY := int(((float64(y2)-float64(y1)-2)*pct)+float64(y1)) + 1 + termbox.SetCell(x2, pctY, '▒', fg, bg) + + termbox.SetCell(x1, y2, '╚', fg, bg) + FillWithChar('═', x1+1, y2, x2-1, y2, fg, bg) + termbox.SetCell(x2, y2, '╝', fg, bg) +} + +func DrawBorderWithTitle(x1, y1, x2, y2 int, title string, fg, bg termbox.Attribute) { + termbox.SetCell(x1, y1, '╔', fg, bg) + + DrawStringAtPoint(title, x1+1, y1, fg, bg) + FillWithChar('═', x1+len(title)+1, y1, x2-1, y1, fg, bg) + termbox.SetCell(x2, y1, '╗', fg, bg) + + FillWithChar('║', x1, y1+1, x1, y2-1, fg, bg) + FillWithChar('║', x2, y1+1, x2, y2-1, fg, bg) + + termbox.SetCell(x1, y2, '╚', fg, bg) + FillWithChar('═', x1+1, y2, x2-1, y2, fg, bg) + termbox.SetCell(x2, y2, '╝', fg, bg) +} + +func DrawBorderWithTitleAndPct(x1, y1, x2, y2 int, title string, pct float64, fg, bg termbox.Attribute) { + termbox.SetCell(x1, y1, '╔', fg, bg) + + DrawStringAtPoint(title, x1+1, y1, fg, bg) + FillWithChar('═', x1+len(title)+1, y1, x2-1, y1, fg, bg) + termbox.SetCell(x2, y1, '╗', fg, bg) + + FillWithChar('║', x1, y1+1, x1, y2-1, fg, bg) + FillWithChar('║', x2, y1+1, x2, y2-1, fg, bg) + // Now the percent indicator + pctY := int(((float64(y2)-float64(y1)-2)*pct)+float64(y1)) + 1 + termbox.SetCell(x2, pctY, '▒', fg, bg) + + termbox.SetCell(x1, y2, '╚', fg, bg) + FillWithChar('═', x1+1, y2, x2-1, y2, fg, bg) + termbox.SetCell(x2, y2, '╝', fg, bg) } // AlignText Aligns the text txt within width characters using the specified alignment @@ -148,4 +203,19 @@ func AlignTextWithFill(txt string, width int, align TextAlignment, fill rune) st } } +func ToLabel(c termboxControl) (*Label, error) { + v, ok := c.(*Label) + if ok { + return v, nil + } + return nil, errors.New("Control isn't a Label") +} +func ToInputField(c termboxControl) (*InputField, error) { + v, ok := c.(*InputField) + if ok { + return v, nil + } + return nil, errors.New("Control isn't an Input Field") +} + /* More advanced things are in their respective files */