Artem Chernyshev 07b7a8c103 feat: change UI component for disks selector
Implemented a new component based on `tview.Table` that shows all
available block devices as a table.

Had to stop using standard form control, because it doesn't really
handle multiline elements.

Signed-off-by: Artem Chernyshev <artem.0xD2@gmail.com>
2020-11-24 13:37:28 -08:00

211 lines
4.1 KiB
Go

// 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 ""
}