xmtop/termui/table.go

222 lines
4.6 KiB
Go

package termui
import (
"fmt"
"image"
"log"
"strings"
"strconv"
. "github.com/gizak/termui/v3"
"github.com/xxxserxxx/lingo"
)
type Table struct {
*Block
Header []string
Rows [][]string
ColWidths []int
ColGap int
PadLeft int
ShowCursor bool
CursorColor Color
ShowLocation bool
UniqueCol int // the column used to uniquely identify each table row
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
ColResizer func()
Tr lingo.Translations
}
// NewTable returns a new Table instance
func NewTable() *Table {
return &Table{
Block: NewBlock(),
SelectedRow: 0,
TopRow: 0,
UniqueCol: 0,
ColResizer: func() {},
}
}
func (self *Table) Draw(buf *Buffer) {
self.Block.Draw(buf)
if self.ShowLocation {
self.drawLocation(buf)
}
self.ColResizer()
// finds exact column starting position
colXPos := []int{}
cur := 1 + self.PadLeft
for _, w := range self.ColWidths {
colXPos = append(colXPos, cur)
cur += w
cur += self.ColGap
}
// 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
if width > (self.Inner.Dx()-colXPos[i])+1 {
continue
}
buf.SetString(
h,
NewStyle(Theme.Default.Fg, ColorClear, ModifierBold),
image.Pt(self.Inner.Min.X+colXPos[i]-1, self.Inner.Min.Y),
)
}
if self.TopRow < 0 {
r := strconv.Itoa(self.TopRow)
log.Printf(self.Tr.Value("error.table", r))
return
}
// prints each row
for rowNum := self.TopRow; rowNum < self.TopRow+self.Inner.Dy()-1 && rowNum < len(self.Rows); rowNum++ {
row := self.Rows[rowNum]
y := (rowNum + 2) - self.TopRow
// prints cursor
style := NewStyle(Theme.Default.Fg)
if self.ShowCursor {
if (self.SelectedItem == "" && rowNum == self.SelectedRow) || (self.SelectedItem != "" && self.SelectedItem == row[self.UniqueCol]) {
style.Fg = self.CursorColor
style.Modifier = ModifierReverse
for _, width := range self.ColWidths {
if width == 0 {
continue
}
buf.SetString(
strings.Repeat(" ", self.Inner.Dx()),
style,
image.Pt(self.Inner.Min.X, self.Inner.Min.Y+y-1),
)
}
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
if width > (self.Inner.Dx()-colXPos[i])+1 {
continue
}
r := TrimString(row[i], width)
buf.SetString(
r,
style,
image.Pt(self.Inner.Min.X+colXPos[i]-1, self.Inner.Min.Y+y-1),
)
}
}
}
func (self *Table) drawLocation(buf *Buffer) {
total := len(self.Rows)
topRow := self.TopRow + 1
if topRow > total {
topRow = total
}
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))
}
// Scrolling ///////////////////////////////////////////////////////////////////
// calcPos is used to calculate the cursor position and the current view into the table.
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
}
if self.SelectedRow > self.TopRow+(self.Inner.Dy()-2) {
self.TopRow = self.SelectedRow - (self.Inner.Dy() - 2)
}
}
func (self *Table) ScrollUp() {
self.SelectedRow--
self.calcPos()
}
func (self *Table) ScrollDown() {
self.SelectedRow++
self.calcPos()
}
func (self *Table) ScrollTop() {
self.SelectedRow = 0
self.calcPos()
}
func (self *Table) ScrollBottom() {
self.SelectedRow = len(self.Rows) - 1
self.calcPos()
}
func (self *Table) ScrollHalfPageUp() {
self.SelectedRow = self.SelectedRow - (self.Inner.Dy()-2)/2
self.calcPos()
}
func (self *Table) ScrollHalfPageDown() {
self.SelectedRow = self.SelectedRow + (self.Inner.Dy()-2)/2
self.calcPos()
}
func (self *Table) ScrollPageUp() {
self.SelectedRow -= (self.Inner.Dy() - 2)
self.calcPos()
}
func (self *Table) ScrollPageDown() {
self.SelectedRow += (self.Inner.Dy() - 2)
self.calcPos()
}
func (self *Table) HandleClick(x, y int) {
x = x - self.Min.X
y = y - self.Min.Y
if (x > 0 && x <= self.Inner.Dx()) && (y > 0 && y <= self.Inner.Dy()) {
self.SelectedRow = (self.TopRow + y) - 2
self.calcPos()
}
}