2019-01-01 07:01:13 +08:00
|
|
|
package termui
|
|
|
|
|
|
|
|
import (
|
2019-03-23 06:03:00 +08:00
|
|
|
"fmt"
|
2019-01-01 08:55:50 +08:00
|
|
|
"image"
|
2019-01-26 12:04:34 +08:00
|
|
|
"log"
|
2019-01-01 07:01:13 +08:00
|
|
|
"strings"
|
2019-01-01 08:55:50 +08:00
|
|
|
|
2019-03-08 15:15:41 +08:00
|
|
|
. "github.com/gizak/termui/v3"
|
2019-01-01 07:01:13 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type Table struct {
|
|
|
|
*Block
|
|
|
|
|
|
|
|
Header []string
|
|
|
|
Rows [][]string
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
ColWidths []int
|
|
|
|
ColGap int
|
|
|
|
PadLeft int
|
2019-01-01 07:01:13 +08:00
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
ShowCursor bool
|
2019-01-24 13:23:35 +08:00
|
|
|
CursorColor Color
|
2019-01-01 07:01:13 +08:00
|
|
|
|
2019-03-23 06:03:00 +08:00
|
|
|
ShowLocation bool
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
UniqueCol int // the column used to uniquely identify each table row
|
2019-01-01 07:01:13 +08:00
|
|
|
SelectedItem string // used to keep the cursor on the correct item if the data changes
|
|
|
|
SelectedRow int
|
|
|
|
TopRow int // used to indicate where in the table we are scrolled at
|
2019-03-01 08:29:52 +08:00
|
|
|
|
|
|
|
ColResizer func()
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewTable returns a new Table instance
|
|
|
|
func NewTable() *Table {
|
2019-03-01 08:29:52 +08:00
|
|
|
return &Table{
|
|
|
|
Block: NewBlock(),
|
2019-01-01 07:01:13 +08:00
|
|
|
SelectedRow: 0,
|
|
|
|
TopRow: 0,
|
|
|
|
UniqueCol: 0,
|
2019-03-01 08:29:52 +08:00
|
|
|
ColResizer: func() {},
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-01 08:55:50 +08:00
|
|
|
func (self *Table) Draw(buf *Buffer) {
|
|
|
|
self.Block.Draw(buf)
|
2019-01-01 07:01:13 +08:00
|
|
|
|
2019-03-23 06:03:00 +08:00
|
|
|
if self.ShowLocation {
|
|
|
|
self.drawLocation(buf)
|
|
|
|
}
|
|
|
|
|
2019-01-01 07:01:13 +08:00
|
|
|
self.ColResizer()
|
|
|
|
|
|
|
|
// finds exact column starting position
|
2019-03-01 08:29:52 +08:00
|
|
|
colXPos := []int{}
|
2019-01-01 07:01:13 +08:00
|
|
|
cur := 1 + self.PadLeft
|
|
|
|
for _, w := range self.ColWidths {
|
2019-03-01 08:29:52 +08:00
|
|
|
colXPos = append(colXPos, cur)
|
2019-01-01 07:01:13 +08:00
|
|
|
cur += w
|
2019-03-01 08:29:52 +08:00
|
|
|
cur += self.ColGap
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// prints header
|
|
|
|
for i, h := range self.Header {
|
|
|
|
width := self.ColWidths[i]
|
|
|
|
if width == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// don't render column if it doesn't fit in widget
|
2019-03-01 08:29:52 +08:00
|
|
|
if width > (self.Inner.Dx()-colXPos[i])+1 {
|
2019-01-01 07:01:13 +08:00
|
|
|
continue
|
|
|
|
}
|
2019-01-01 08:55:50 +08:00
|
|
|
buf.SetString(
|
|
|
|
h,
|
2019-01-24 13:23:35 +08:00
|
|
|
NewStyle(Theme.Default.Fg, ColorClear, ModifierBold),
|
2019-03-01 08:29:52 +08:00
|
|
|
image.Pt(self.Inner.Min.X+colXPos[i]-1, self.Inner.Min.Y),
|
2019-01-01 08:55:50 +08:00
|
|
|
)
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
|
2019-01-26 12:04:34 +08:00
|
|
|
if self.TopRow < 0 {
|
|
|
|
log.Printf("table widget TopRow value less than 0. TopRow: %v", self.TopRow)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-01 07:01:13 +08:00
|
|
|
// prints each row
|
2019-01-01 08:55:50 +08:00
|
|
|
for rowNum := self.TopRow; rowNum < self.TopRow+self.Inner.Dy()-1 && rowNum < len(self.Rows); rowNum++ {
|
2019-01-01 07:01:13 +08:00
|
|
|
row := self.Rows[rowNum]
|
|
|
|
y := (rowNum + 2) - self.TopRow
|
|
|
|
|
|
|
|
// prints cursor
|
2019-01-24 13:23:35 +08:00
|
|
|
style := NewStyle(Theme.Default.Fg)
|
2019-03-01 08:29:52 +08:00
|
|
|
if self.ShowCursor {
|
2019-01-01 07:01:13 +08:00
|
|
|
if (self.SelectedItem == "" && rowNum == self.SelectedRow) || (self.SelectedItem != "" && self.SelectedItem == row[self.UniqueCol]) {
|
2019-01-24 13:23:35 +08:00
|
|
|
style.Fg = self.CursorColor
|
|
|
|
style.Modifier = ModifierReverse
|
2019-01-01 07:01:13 +08:00
|
|
|
for _, width := range self.ColWidths {
|
|
|
|
if width == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2019-01-01 08:55:50 +08:00
|
|
|
buf.SetString(
|
|
|
|
strings.Repeat(" ", self.Inner.Dx()),
|
2019-01-24 13:23:35 +08:00
|
|
|
style,
|
2019-01-15 11:47:44 +08:00
|
|
|
image.Pt(self.Inner.Min.X, self.Inner.Min.Y+y-1),
|
2019-01-01 08:55:50 +08:00
|
|
|
)
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
self.SelectedItem = row[self.UniqueCol]
|
|
|
|
self.SelectedRow = rowNum
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// prints each col of the row
|
|
|
|
for i, width := range self.ColWidths {
|
|
|
|
if width == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// don't render column if width is greater than distance to end of widget
|
2019-03-01 08:29:52 +08:00
|
|
|
if width > (self.Inner.Dx()-colXPos[i])+1 {
|
2019-01-01 07:01:13 +08:00
|
|
|
continue
|
|
|
|
}
|
2019-01-01 08:55:50 +08:00
|
|
|
r := TrimString(row[i], width)
|
|
|
|
buf.SetString(
|
|
|
|
r,
|
2019-01-24 13:23:35 +08:00
|
|
|
style,
|
2019-03-01 08:29:52 +08:00
|
|
|
image.Pt(self.Inner.Min.X+colXPos[i]-1, self.Inner.Min.Y+y-1),
|
2019-01-01 08:55:50 +08:00
|
|
|
)
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-23 06:03:00 +08:00
|
|
|
func (self *Table) drawLocation(buf *Buffer) {
|
|
|
|
total := len(self.Rows)
|
|
|
|
topRow := self.TopRow + 1
|
2019-06-04 06:18:40 +08:00
|
|
|
if topRow > total {
|
|
|
|
topRow = total
|
|
|
|
}
|
2019-03-23 06:03:00 +08:00
|
|
|
bottomRow := self.TopRow + self.Inner.Dy() - 1
|
|
|
|
if bottomRow > total {
|
|
|
|
bottomRow = total
|
|
|
|
}
|
|
|
|
|
|
|
|
loc := fmt.Sprintf(" %d - %d of %d ", topRow, bottomRow, total)
|
|
|
|
|
|
|
|
width := len(loc)
|
|
|
|
buf.SetString(loc, self.TitleStyle, image.Pt(self.Max.X-width-2, self.Min.Y))
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
// Scrolling ///////////////////////////////////////////////////////////////////
|
2019-01-01 07:01:13 +08:00
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
// calcPos is used to calculate the cursor position and the current view into the table.
|
2019-01-01 07:01:13 +08:00
|
|
|
func (self *Table) calcPos() {
|
|
|
|
self.SelectedItem = ""
|
|
|
|
|
|
|
|
if self.SelectedRow < 0 {
|
|
|
|
self.SelectedRow = 0
|
|
|
|
}
|
|
|
|
if self.SelectedRow < self.TopRow {
|
|
|
|
self.TopRow = self.SelectedRow
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.SelectedRow > len(self.Rows)-1 {
|
|
|
|
self.SelectedRow = len(self.Rows) - 1
|
|
|
|
}
|
2019-01-01 08:55:50 +08:00
|
|
|
if self.SelectedRow > self.TopRow+(self.Inner.Dy()-2) {
|
|
|
|
self.TopRow = self.SelectedRow - (self.Inner.Dy() - 2)
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollUp() {
|
2019-01-26 12:04:34 +08:00
|
|
|
self.SelectedRow--
|
2019-01-01 07:01:13 +08:00
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollDown() {
|
2019-01-26 12:04:34 +08:00
|
|
|
self.SelectedRow++
|
2019-01-01 07:01:13 +08:00
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollTop() {
|
2019-01-01 07:01:13 +08:00
|
|
|
self.SelectedRow = 0
|
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollBottom() {
|
2019-01-01 07:01:13 +08:00
|
|
|
self.SelectedRow = len(self.Rows) - 1
|
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollHalfPageUp() {
|
2019-01-01 08:55:50 +08:00
|
|
|
self.SelectedRow = self.SelectedRow - (self.Inner.Dy()-2)/2
|
2019-01-01 07:01:13 +08:00
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollHalfPageDown() {
|
2019-01-01 08:55:50 +08:00
|
|
|
self.SelectedRow = self.SelectedRow + (self.Inner.Dy()-2)/2
|
2019-01-01 07:01:13 +08:00
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollPageUp() {
|
2019-01-01 08:55:50 +08:00
|
|
|
self.SelectedRow -= (self.Inner.Dy() - 2)
|
2019-01-01 07:01:13 +08:00
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) ScrollPageDown() {
|
2019-01-01 08:55:50 +08:00
|
|
|
self.SelectedRow += (self.Inner.Dy() - 2)
|
2019-01-01 07:01:13 +08:00
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
|
2019-03-01 08:29:52 +08:00
|
|
|
func (self *Table) HandleClick(x, y int) {
|
2019-01-01 08:55:50 +08:00
|
|
|
x = x - self.Min.X
|
|
|
|
y = y - self.Min.Y
|
|
|
|
if (x > 0 && x <= self.Inner.Dx()) && (y > 0 && y <= self.Inner.Dy()) {
|
2019-01-01 07:01:13 +08:00
|
|
|
self.SelectedRow = (self.TopRow + y) - 2
|
|
|
|
self.calcPos()
|
|
|
|
}
|
|
|
|
}
|