cordless/ui/shortcutdialog/table.go

227 lines
6.2 KiB
Go

package shortcutdialog
import (
"fmt"
"strings"
"github.com/Bios-Marcel/cordless/shortcuts"
"github.com/Bios-Marcel/cordless/tview"
tcell "github.com/gdamore/tcell/v2"
)
const (
actionCellIndex int = iota
scopeCellIndex
shortcutCellIndex
)
// ShortcutTable is a component that displays shortcuts and allows changing
// them.
type ShortcutTable struct {
table *tview.Table
shortcuts []*shortcuts.Shortcut
selection int
focusNext func()
focusPrevious func()
}
// NewShortcutTable creates a new shortcut table that doesn't contain any data.
func NewShortcutTable() *ShortcutTable {
table := tview.NewTable()
shortcutsTable := &ShortcutTable{
table: table,
selection: -1,
}
table.SetSelectable(true, false)
table.SetBorder(true)
//Header + emptyrow
table.SetFixed(2, 3)
table.SetCell(0, actionCellIndex, createHeaderCell("Action"))
table.SetCell(0, scopeCellIndex, createHeaderCell("Scope"))
table.SetCell(0, shortcutCellIndex, createHeaderCell("Shortcut"))
table.SetInputCapture(shortcutsTable.handleInput)
return shortcutsTable
}
// GetPrimitive returns the primitive to be put onto a layout.
func (shortcutTable *ShortcutTable) GetPrimitive() tview.Primitive {
return shortcutTable.table
}
func createHeaderCell(text string) *tview.TableCell {
return tview.NewTableCell(text).
SetSelectable(false).
SetAlign(tview.AlignCenter).
SetExpansion(1).
SetMaxWidth(1)
}
// SetShortcuts sets the shortcut data and changes the UI accordingly.
func (shortcutTable *ShortcutTable) SetShortcuts(shortcuts []*shortcuts.Shortcut) {
shortcutTable.shortcuts = shortcuts
shortcutTable.selection = -1
row, _ := shortcutTable.table.GetFixed()
//Using clear will remove the content of the fixed rows, therefore we
// manually remove starting from the first non-fixed row.
for index := row; index < shortcutTable.table.GetRowCount(); index++ {
shortcutTable.table.RemoveRow(index)
}
for _, shortcut := range shortcuts {
nameCell := tview.NewTableCell(shortcut.Name).
SetExpansion(1).
SetMaxWidth(1)
shortcutTable.table.SetCell(row, actionCellIndex, nameCell)
scopeCell := tview.NewTableCell(shortcut.Scope.Name).
SetExpansion(1).
SetMaxWidth(1)
shortcutTable.table.SetCell(row, scopeCellIndex, scopeCell)
eventCell := tview.NewTableCell(EventToString(shortcut.Event)).
SetExpansion(1).
SetMaxWidth(1)
shortcutTable.table.SetCell(row, shortcutCellIndex, eventCell)
row++
}
}
// GetShortcuts returns the array containing the currently displayed shortcuts.
func (shortcutTable *ShortcutTable) GetShortcuts() []*shortcuts.Shortcut {
return shortcutTable.shortcuts
}
// SetFocusNext decides which component will be focused when hitting tab
func (shortcutTable *ShortcutTable) SetFocusNext(function func()) {
shortcutTable.focusNext = function
}
// SetFocusPrevious decides which component will be focused when hitting
// shift+tab
func (shortcutTable *ShortcutTable) SetFocusPrevious(function func()) {
shortcutTable.focusPrevious = function
}
func (shortcutTable *ShortcutTable) handleInput(event *tcell.EventKey) *tcell.EventKey {
if shortcutTable.selection == -1 {
if event.Key() == tcell.KeyTab {
if shortcutTable.focusNext != nil {
shortcutTable.focusNext()
}
} else if event.Key() == tcell.KeyBacktab {
if shortcutTable.focusPrevious != nil {
shortcutTable.focusPrevious()
}
}
if event.Key() == tcell.KeyUp || event.Key() == tcell.KeyDown {
return event
}
}
firstNonFixedRow, _ := shortcutTable.table.GetFixed()
selectedRow, _ := shortcutTable.table.GetSelection()
//The first row of the table isn't the first row containing data
dataIndex := selectedRow - firstNonFixedRow
if shortcutTable.selection == -1 && selectedRow >= firstNonFixedRow {
if event.Key() == tcell.KeyEnter {
if selectedRow != -1 {
shortcutTable.table.GetCell(selectedRow, shortcutCellIndex).SetText("[blue][::ub]Hit the desired keycombination")
shortcutTable.selection = selectedRow
}
} else if event.Key() == tcell.KeyRune && event.Rune() == 'r' && event.Modifiers() == tcell.ModNone {
shortcut := shortcutTable.shortcuts[dataIndex]
shortcut.Reset()
shortcutTable.table.GetCell(selectedRow, shortcutCellIndex).SetText(EventToString(shortcut.Event))
persistError := shortcuts.Persist()
if persistError != nil {
panic(persistError)
}
} else if event.Key() == tcell.KeyBackspace || event.Key() == tcell.KeyBackspace2 {
shortcutTable.table.GetCell(selectedRow, shortcutCellIndex).SetText("")
shortcutTable.shortcuts[dataIndex].Event = nil
persistError := shortcuts.Persist()
if persistError != nil {
panic(persistError)
}
}
} else if selectedRow >= firstNonFixedRow && shortcutTable.selection != -1 {
shortcutTable.table.GetCell(selectedRow, shortcutCellIndex).SetText(EventToString(event))
shortcutTable.shortcuts[dataIndex].Event = tcell.NewEventKey(
event.Key(),
event.Rune(),
event.Modifiers())
persistError := shortcuts.Persist()
if persistError != nil {
panic(persistError)
}
shortcutTable.selection = -1
}
return nil
}
// EventToString renders a tcell.EventKey as a human readable string
func EventToString(event *tcell.EventKey) string {
if event == nil {
return ""
}
var m []string
if event.Modifiers()&tcell.ModCtrl != 0 {
m = append(m, "Ctrl")
}
if event.Modifiers()&tcell.ModShift != 0 {
m = append(m, "Shift")
}
if event.Modifiers()&tcell.ModAlt != 0 {
m = append(m, "Alt")
}
if event.Modifiers()&tcell.ModMeta != 0 {
m = append(m, "Meta")
}
var (
s string
ok bool
)
if s, ok = tcell.KeyNames[event.Key()]; !ok {
if event.Key() == tcell.KeyRune {
if event.Rune() >= 'A' && event.Rune() <= 'Z' {
s = "Shift+" + string(event.Rune())
} else {
s = strings.ToUpper(string(event.Rune()))
}
} else {
s = fmt.Sprintf("Key[%d,%d]", event.Key(), int(event.Rune()))
}
}
if len(m) != 0 {
if event.Modifiers()&tcell.ModCtrl != 0 && strings.HasPrefix(s, "Ctrl-") {
s = s[5:]
}
return fmt.Sprintf("%s+%s", strings.Join(m, "+"), s)
}
return tview.Escape(s)
}
// IsDefiningShortcut indicates whether the user is currently selecting a
// shortcut for any function.
func (shortcutTable *ShortcutTable) IsDefiningShortcut() bool {
return shortcutTable.selection != -1
}