diff options
Diffstat (limited to 'widgets/table.go')
-rw-r--r-- | widgets/table.go | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/widgets/table.go b/widgets/table.go new file mode 100644 index 0000000..7182c03 --- /dev/null +++ b/widgets/table.go @@ -0,0 +1,221 @@ +package widgets + +import ( + "fmt" + "image" + "log" + "strconv" + "strings" + + "github.com/gizak/termui/v3" + "github.com/xxxserxxx/lingo/v2" +) + +type Table struct { + *termui.Block + + Header []string + Rows [][]string + + ColWidths []int + ColGap int + PadLeft int + + ShowCursor bool + CursorColor termui.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: termui.NewBlock(), + SelectedRow: 0, + TopRow: 0, + UniqueCol: 0, + ColResizer: func() {}, + } +} + +func (self *Table) Draw(buf *termui.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, + termui.NewStyle(termui.Theme.Default.Fg, termui.ColorClear, termui.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 := termui.NewStyle(termui.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 = termui.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 := termui.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 *termui.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() + } +} |