summaryrefslogtreecommitdiffstats
path: root/widgets/table.go
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/table.go')
-rw-r--r--widgets/table.go221
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()
+ }
+}