225 lines
4.7 KiB
Go
225 lines
4.7 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright Authors of K9s
|
|
|
|
package ui
|
|
|
|
import (
|
|
"github.com/derailed/tcell/v2"
|
|
"github.com/derailed/tview"
|
|
)
|
|
|
|
// SelectTable represents a table with selections.
|
|
type SelectTable struct {
|
|
*tview.Table
|
|
|
|
model Tabular
|
|
selectedFn func(string) string
|
|
marks map[string]struct{}
|
|
selFgColor tcell.Color
|
|
selBgColor tcell.Color
|
|
}
|
|
|
|
// SetModel sets the table model.
|
|
func (s *SelectTable) SetModel(m Tabular) {
|
|
s.model = m
|
|
}
|
|
|
|
// GetModel returns the current model.
|
|
func (s *SelectTable) GetModel() Tabular {
|
|
return s.model
|
|
}
|
|
|
|
// ClearSelection reset selected row.
|
|
func (s *SelectTable) ClearSelection() {
|
|
s.Select(0, 0)
|
|
s.ScrollToBeginning()
|
|
}
|
|
|
|
// SelectFirstRow select first data row if any.
|
|
func (s *SelectTable) SelectFirstRow() {
|
|
if s.GetRowCount() > 0 {
|
|
s.Select(1, 0)
|
|
}
|
|
}
|
|
|
|
// GetSelectedItems return currently marked or selected items names.
|
|
func (s *SelectTable) GetSelectedItems() []string {
|
|
if len(s.marks) == 0 {
|
|
if item := s.GetSelectedItem(); item != "" {
|
|
return []string{item}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
items := make([]string, 0, len(s.marks))
|
|
for item := range s.marks {
|
|
items = append(items, item)
|
|
}
|
|
|
|
return items
|
|
}
|
|
|
|
// GetRowID returns the row id at at given location.
|
|
func (s *SelectTable) GetRowID(index int) (string, bool) {
|
|
cell := s.GetCell(index, 0)
|
|
if cell == nil {
|
|
return "", false
|
|
}
|
|
id, ok := cell.GetReference().(string)
|
|
|
|
return id, ok
|
|
}
|
|
|
|
// GetSelectedItem returns the currently selected item name.
|
|
func (s *SelectTable) GetSelectedItem() string {
|
|
if s.GetSelectedRowIndex() == 0 || s.model.Empty() {
|
|
return ""
|
|
}
|
|
sel, ok := s.GetCell(s.GetSelectedRowIndex(), 0).GetReference().(string)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
if s.selectedFn != nil {
|
|
return s.selectedFn(sel)
|
|
}
|
|
return sel
|
|
}
|
|
|
|
// GetSelectedCell returns the content of a cell for the currently selected row.
|
|
func (s *SelectTable) GetSelectedCell(col int) string {
|
|
r, _ := s.GetSelection()
|
|
return TrimCell(s, r, col)
|
|
}
|
|
|
|
// SetSelectedFn defines a function that cleanse the current selection.
|
|
func (s *SelectTable) SetSelectedFn(f func(string) string) {
|
|
s.selectedFn = f
|
|
}
|
|
|
|
// GetSelectedRowIndex fetch the currently selected row index.
|
|
func (s *SelectTable) GetSelectedRowIndex() int {
|
|
r, _ := s.GetSelection()
|
|
return r
|
|
}
|
|
|
|
// SelectRow select a given row by index.
|
|
func (s *SelectTable) SelectRow(r, c int, broadcast bool) {
|
|
if !broadcast {
|
|
s.SetSelectionChangedFunc(nil)
|
|
}
|
|
if c := s.model.Count(); c > 0 && r-1 > c {
|
|
r = c + 1
|
|
}
|
|
defer s.SetSelectionChangedFunc(s.selectionChanged)
|
|
s.Select(r, c)
|
|
}
|
|
|
|
// UpdateSelection refresh selected row.
|
|
func (s *SelectTable) updateSelection(broadcast bool) {
|
|
r, c := s.GetSelection()
|
|
s.SelectRow(r, c, broadcast)
|
|
}
|
|
|
|
func (s *SelectTable) selectionChanged(r, c int) {
|
|
if r < 0 {
|
|
return
|
|
}
|
|
if cell := s.GetCell(r, c); cell != nil {
|
|
s.SetSelectedStyle(
|
|
tcell.StyleDefault.Foreground(s.selFgColor).
|
|
Background(cell.Color).Attributes(tcell.AttrBold))
|
|
}
|
|
}
|
|
|
|
// ClearMarks delete all marked items.
|
|
func (s *SelectTable) ClearMarks() {
|
|
for k := range s.marks {
|
|
delete(s.marks, k)
|
|
}
|
|
}
|
|
|
|
// DeleteMark delete a marked item.
|
|
func (s *SelectTable) DeleteMark(k string) {
|
|
delete(s.marks, k)
|
|
}
|
|
|
|
// ToggleMark toggles marked row.
|
|
func (s *SelectTable) ToggleMark() {
|
|
sel := s.GetSelectedItem()
|
|
if sel == "" {
|
|
return
|
|
}
|
|
if _, ok := s.marks[sel]; ok {
|
|
delete(s.marks, s.GetSelectedItem())
|
|
} else {
|
|
s.marks[sel] = struct{}{}
|
|
}
|
|
|
|
if cell := s.GetCell(s.GetSelectedRowIndex(), 0); cell != nil {
|
|
s.SetSelectedStyle(tcell.StyleDefault.Foreground(cell.BackgroundColor).Background(cell.Color).Attributes(tcell.AttrBold))
|
|
}
|
|
}
|
|
|
|
// SpanMark toggles marked row.
|
|
func (s *SelectTable) SpanMark() {
|
|
selIndex, prev := s.GetSelectedRowIndex(), -1
|
|
if selIndex <= 0 {
|
|
return
|
|
}
|
|
// Look back to find previous mark
|
|
for i := selIndex - 1; i > 0; i-- {
|
|
id, ok := s.GetRowID(i)
|
|
if !ok {
|
|
break
|
|
}
|
|
if _, ok := s.marks[id]; ok {
|
|
prev = i
|
|
break
|
|
}
|
|
}
|
|
if prev != -1 {
|
|
s.markRange(prev, selIndex)
|
|
return
|
|
}
|
|
|
|
// Look forward to see if we have a mark
|
|
for i := selIndex; i < s.GetRowCount(); i++ {
|
|
id, ok := s.GetRowID(i)
|
|
if !ok {
|
|
break
|
|
}
|
|
if _, ok := s.marks[id]; ok {
|
|
prev = i
|
|
break
|
|
}
|
|
}
|
|
s.markRange(prev, selIndex)
|
|
}
|
|
|
|
func (s *SelectTable) markRange(prev, curr int) {
|
|
if prev < 0 {
|
|
return
|
|
}
|
|
if prev > curr {
|
|
prev, curr = curr, prev
|
|
}
|
|
for i := prev + 1; i <= curr; i++ {
|
|
id, ok := s.GetRowID(i)
|
|
if !ok {
|
|
break
|
|
}
|
|
s.marks[id] = struct{}{}
|
|
cell := s.GetCell(s.GetSelectedRowIndex(), 0)
|
|
if cell == nil {
|
|
break
|
|
}
|
|
s.SetSelectedStyle(tcell.StyleDefault.Foreground(cell.BackgroundColor).Background(cell.Color).Attributes(tcell.AttrBold))
|
|
}
|
|
}
|
|
|
|
// IsMarked returns true if this item was marked.
|
|
func (s *Table) IsMarked(item string) bool {
|
|
_, ok := s.marks[item]
|
|
return ok
|
|
}
|