2016-02-09 16:21:57 +00:00
|
|
|
package termboxUtil
|
|
|
|
|
2020-02-20 16:08:19 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/nsf/termbox-go"
|
|
|
|
)
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// Frame is a frame for holding other elements
|
|
|
|
// It manages it's own x/y, tab index
|
|
|
|
type Frame struct {
|
|
|
|
id string
|
|
|
|
x, y, width, height int
|
|
|
|
tabIdx int
|
|
|
|
fg, bg termbox.Attribute
|
2019-03-25 15:10:25 +00:00
|
|
|
activeFg, activeBg termbox.Attribute
|
2016-02-09 16:21:57 +00:00
|
|
|
bordered bool
|
|
|
|
controls []termboxControl
|
|
|
|
tabSkip bool
|
2019-03-25 15:10:25 +00:00
|
|
|
active bool
|
|
|
|
title string
|
2020-02-20 16:08:19 +00:00
|
|
|
status string
|
|
|
|
rightStatus string
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateFrame creates a Frame at x, y that is w by h
|
|
|
|
func CreateFrame(x, y, w, h int, fg, bg termbox.Attribute) *Frame {
|
2019-03-25 15:10:25 +00:00
|
|
|
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 }
|
|
|
|
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetStatus(status string) { c.status = status }
|
|
|
|
|
2019-03-25 15:10:25 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) IsActive() bool { return c.active }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// GetID returns this control's ID
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetID() string { return c.id }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetID sets this control's ID
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetID(newID string) { c.id = newID }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// GetX returns the x position of the frame
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetX() int { return c.x }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetX sets the x position of the frame
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetX(x int) { c.x = x }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// GetY returns the y position of the frame
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetY() int { return c.y }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetY sets the y position of the frame
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetY(y int) { c.y = y }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// GetWidth returns the current width of the frame
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetWidth() int { return c.width }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetWidth sets the current width of the frame
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetWidth(w int) { c.width = w }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// GetHeight returns the current height of the frame
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetHeight() int { return c.height }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetHeight sets the current height of the frame
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetHeight(h int) { c.height = h }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// GetFgColor returns the foreground color
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetFgColor() termbox.Attribute { return c.fg }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetFgColor sets the foreground color
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetFgColor(fg termbox.Attribute) { c.fg = fg }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// GetBgColor returns the background color
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetBgColor() termbox.Attribute { return c.bg }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetBgColor sets the current background color
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetBgColor(bg termbox.Attribute) { c.bg = bg }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// IsBordered returns true or false if this frame has a border
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) IsBordered() bool { return c.bordered }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetBordered sets whether we render a border around the frame
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetBordered(b bool) { c.bordered = b }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// IsTabSkipped returns whether this modal has it's tabskip flag set
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) IsTabSkipped() bool { return c.tabSkip }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// SetTabSkip sets the tabskip flag for this control
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) SetTabSkip(b bool) { c.tabSkip = b }
|
2016-02-09 16:21:57 +00:00
|
|
|
|
|
|
|
// AddControl adds a control to the frame
|
2020-02-20 16:08:19 +00:00
|
|
|
func (c *Frame) AddControl(t termboxControl) { c.controls = append(c.controls, t) }
|
2019-03-25 15:10:25 +00:00
|
|
|
|
|
|
|
func (c *Frame) ResetTabIndex() {
|
|
|
|
for k, v := range c.controls {
|
|
|
|
if !v.IsTabSkipped() {
|
|
|
|
c.tabIdx = k
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetActiveControl returns the control at tabIdx
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetActiveControl() termboxControl {
|
|
|
|
if len(c.controls) >= c.tabIdx {
|
|
|
|
if c.controls[c.tabIdx].IsTabSkipped() {
|
|
|
|
c.FindNextTabStop()
|
|
|
|
}
|
|
|
|
return c.controls[c.tabIdx]
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetControls returns a slice of all controls
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetControls() []termboxControl {
|
|
|
|
return c.controls
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetControl returns the control at index i
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetControl(idx int) termboxControl {
|
|
|
|
if len(c.controls) >= idx {
|
|
|
|
return c.controls[idx]
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetControlCount returns the number of controls contained
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetControlCount() int {
|
|
|
|
return len(c.controls)
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetLastControl returns the last control contained
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetLastControl() termboxControl {
|
|
|
|
return c.controls[len(c.controls)-1]
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-03-05 13:54:26 +00:00
|
|
|
// RemoveAllControls clears the control slice
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) RemoveAllControls() {
|
|
|
|
c.controls = []termboxControl{}
|
2019-03-05 13:54:26 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:21:57 +00:00
|
|
|
// DrawControl figures out the relative position of the control,
|
|
|
|
// sets it, draws it, then resets it.
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) DrawControl(t termboxControl) {
|
2016-02-09 16:21:57 +00:00
|
|
|
ctlX, ctlY := t.GetX(), t.GetY()
|
2019-03-25 15:10:25 +00:00
|
|
|
t.SetX((c.GetX() + ctlX))
|
|
|
|
t.SetY((c.GetY() + ctlY))
|
2016-02-09 16:21:57 +00:00
|
|
|
t.Draw()
|
|
|
|
t.SetX(ctlX)
|
|
|
|
t.SetY(ctlY)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBottomY returns the y of the lowest control in the frame
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) GetBottomY() int {
|
2016-02-09 16:21:57 +00:00
|
|
|
var ret int
|
2019-03-25 15:10:25 +00:00
|
|
|
for idx := range c.controls {
|
|
|
|
if c.controls[idx].GetY()+c.controls[idx].GetHeight() > ret {
|
|
|
|
ret = c.controls[idx].GetY() + c.controls[idx].GetHeight()
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleEvent accepts the termbox event and returns whether it was consumed
|
2019-03-25 15:10:25 +00:00
|
|
|
func (c *Frame) HandleEvent(event termbox.Event) bool {
|
2020-02-20 16:08:19 +00:00
|
|
|
// If the currently active control consumes the event, we don't need to handle it
|
2020-02-13 16:20:49 +00:00
|
|
|
if c.controls[c.tabIdx].HandleEvent(event) {
|
2020-02-20 16:08:19 +00:00
|
|
|
c.rightStatus = fmt.Sprintf("C (%d/%d)", c.tabIdx, len(c.controls))
|
2020-02-13 16:20:49 +00:00
|
|
|
return true
|
|
|
|
}
|
2020-02-20 16:08:19 +00:00
|
|
|
// All that a frame cares about is tabbing around
|
2016-02-19 14:51:24 +00:00
|
|
|
if event.Key == termbox.KeyTab {
|
2020-02-13 16:20:49 +00:00
|
|
|
ret := !c.IsOnLastControl()
|
2019-03-25 15:10:25 +00:00
|
|
|
c.FindNextTabStop()
|
2020-02-20 16:08:19 +00:00
|
|
|
c.rightStatus = fmt.Sprintf("N (%d/%d)", c.tabIdx, len(c.controls))
|
2020-02-13 16:20:49 +00:00
|
|
|
return ret
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
2020-02-20 16:08:19 +00:00
|
|
|
c.rightStatus = fmt.Sprintf("B (%d/%d)", c.tabIdx, len(c.controls))
|
2020-02-13 16:20:49 +00:00
|
|
|
return false
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindNextTabStop finds the next control that can be tabbed to
|
|
|
|
// A return of true means it found a different one than we started on.
|
2019-03-25 15:10:25 +00:00
|
|
|
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 {
|
2016-02-09 16:21:57 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2019-03-25 15:10:25 +00:00
|
|
|
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
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw outputs the Scoll Frame on the screen
|
2019-03-25 15:10:25 +00:00
|
|
|
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)
|
|
|
|
}
|
2016-02-09 16:21:57 +00:00
|
|
|
maxWidth--
|
|
|
|
maxHeight--
|
|
|
|
x++
|
|
|
|
y++
|
|
|
|
startX++
|
|
|
|
startY++
|
|
|
|
}
|
2019-03-25 15:10:25 +00:00
|
|
|
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])
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|
2020-02-20 16:08:19 +00:00
|
|
|
if c.status != "" {
|
|
|
|
DrawStringAtPoint(" "+c.status+" ", c.x+1, c.y+c.height, borderFg, borderBg)
|
|
|
|
}
|
|
|
|
if c.rightStatus != "" {
|
|
|
|
DrawStringAtPoint(" "+c.rightStatus+" ", (c.x+c.width)-len(c.rightStatus)-2, c.y+c.height, borderFg, borderBg)
|
|
|
|
}
|
2016-02-09 16:21:57 +00:00
|
|
|
}
|