// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) var ( backgroundColor = tcell.Color235 textNormalColor = tcell.ColorIvory selectedTextColor = tview.Styles.PrimaryTextColor selectedBackgroundColor = tview.Styles.ContrastBackgroundColor ) // NewTable creates new table. func NewTable() *Table { t := &Table{ Table: tview.NewTable(), selectedRow: -1, hoveredRow: -1, rows: [][]interface{}{}, } hasFocus := false t.SetDrawFunc(func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { changed := hasFocus != t.HasFocus() if !changed { return x, y, width, height } hasFocus = t.HasFocus() if hasFocus { if t.selectedRow != -1 { t.HoverRow(t.selectedRow) } else { t.HoverRow(1) } } else { t.HoverRow(-1) } return x, y, width, height }) t.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey { // nolint:exhaustive switch e.Key() { case tcell.KeyUp: if t.hoveredRow > 0 { t.HoverRow(t.hoveredRow - 1) } case tcell.KeyDown: if t.hoveredRow < t.GetRowCount() { t.HoverRow(t.hoveredRow + 1) } case tcell.KeyEnter: if t.hoveredRow != -1 { t.SelectRow(t.hoveredRow) } } return e }) return t } // Table list of choices represented in table format. type Table struct { *tview.Table selectedRow int hoveredRow int onRowSelected func(row int) rows [][]interface{} } // SetHeader sets table header. func (t *Table) SetHeader(keys ...interface{}) { t.AddRow(keys...) } // AddRow adds a new row to the table. func (t *Table) AddRow(columns ...interface{}) { row := t.GetRowCount() col := backgroundColor textColor := tview.Styles.PrimaryTextColor if row == 0 { col = tcell.ColorSilver textColor = tview.Styles.InverseTextColor } else { t.rows = append(t.rows, columns) } cell := tview.NewTableCell(" "). SetAlign(tview.AlignCenter). SetBackgroundColor(col). SetTextColor(textColor) if row > 0 { cell.SetClickedFunc(func() bool { t.HoverRow(row) t.SelectRow(row) return true }) } t.SetCell(row, 0, cell) for i, text := range columns { cell = tview.NewTableCell(text.(string)) cell.SetExpansion(1) cell.SetTextColor(textColor) if i == len(columns)-1 { cell.SetAlign(tview.AlignRight) } cell.SetBackgroundColor(col) cell.SetSelectable(true) t.SetCell(row, i+1, cell) if row > 0 { cell.SetClickedFunc(func() bool { return t.HoverRow(row) }) } } } // SelectRow selects the row in the table. func (t *Table) SelectRow(row int) bool { // don't select the header if row < 2 { row = 1 } if row < t.GetRowCount() { if t.selectedRow != -1 { t.GetCell(t.selectedRow, 0).SetText(" ") } t.GetCell(row, 0).SetText("►") t.selectedRow = row if t.onRowSelected != nil { t.onRowSelected(row) } return true } return false } // HoverRow highlights the row in the table. func (t *Table) HoverRow(row int) bool { updateRowStyle := func(r int, foregroundColor, backgroundColor tcell.Color) { for i := 0; i < t.GetColumnCount(); i++ { t.GetCell(r, i).SetBackgroundColor(backgroundColor).SetTextColor(foregroundColor) } } // don't select the header if row == 0 { row = 1 } if row < t.GetRowCount() { if t.hoveredRow != -1 { updateRowStyle(t.hoveredRow, textNormalColor, backgroundColor) } if row != -1 { updateRowStyle(row, selectedTextColor, selectedBackgroundColor) } t.hoveredRow = row return true } return false } // GetHeight implements Multiline interface. func (t *Table) GetHeight() int { return t.GetRowCount() } // SetRowSelectedFunc called when selected row is updated. func (t *Table) SetRowSelectedFunc(callback func(row int)) { t.onRowSelected = callback } // GetValue returns value in row/column. func (t *Table) GetValue(row, column int) interface{} { if row < len(t.rows) && column < len(t.rows[row]) { return t.rows[row][column] } return "" }