package dialogs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.brettb.xyz/goinv/client/internal/ui/style"
|
|
"git.brettb.xyz/goinv/client/internal/ui/utils"
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const (
|
|
cmdWidthOffset = 6
|
|
)
|
|
|
|
const (
|
|
cmdTableFocus = 0 + iota
|
|
cmdFormFocus
|
|
)
|
|
|
|
type CommandDialog struct {
|
|
*tview.Box
|
|
layout *tview.Flex
|
|
table *tview.Table
|
|
form *tview.Form
|
|
logger *zap.Logger
|
|
display bool
|
|
options [][]string
|
|
width int
|
|
height int
|
|
focusElement int
|
|
selectedStyle tcell.Style
|
|
cancelHandler func()
|
|
selectHandler func()
|
|
}
|
|
|
|
func NewCommandDialog(logger *zap.Logger, options [][]string) *CommandDialog {
|
|
form := tview.NewForm().
|
|
AddButton("Cancel", nil).
|
|
SetButtonsAlign(tview.AlignRight)
|
|
|
|
form.SetBackgroundColor(style.DialogBgColor)
|
|
form.SetButtonBackgroundColor(style.ButtonBgColor)
|
|
form.SetButtonTextColor(style.ButtonFgColor)
|
|
|
|
activatedStyle := tcell.StyleDefault.
|
|
Background(style.ButtonSelectedBgColor).
|
|
Foreground(style.ButtonSelectedFgColor)
|
|
|
|
form.SetButtonActivatedStyle(activatedStyle)
|
|
|
|
cmdsTable := tview.NewTable()
|
|
cmdsTable.SetBackgroundColor(style.DialogBgColor)
|
|
|
|
cmdWidth := 0
|
|
|
|
cmdsTable.SetCell(0, 0,
|
|
tview.NewTableCell(fmt.Sprintf("[%s::b]COMMAND", style.GetColorHex(style.TableHeaderFgColor))).
|
|
SetExpansion(1).
|
|
SetBackgroundColor(style.TableHeaderBgColor).
|
|
SetTextColor(style.TableHeaderFgColor).
|
|
SetAlign(tview.AlignLeft).
|
|
SetSelectable(false))
|
|
|
|
cmdsTable.SetCell(0, 1,
|
|
tview.NewTableCell(fmt.Sprintf("[%s::b]DESCRIPTION", style.GetColorHex(style.TableHeaderFgColor))).
|
|
SetExpansion(1).
|
|
SetBackgroundColor(style.TableHeaderBgColor).
|
|
SetTextColor(style.TableHeaderFgColor).
|
|
SetAlign(tview.AlignCenter).
|
|
SetSelectable(false))
|
|
|
|
col1Width := 0
|
|
col2Width := 0
|
|
|
|
for i, option := range options {
|
|
cmdsTable.SetCell(i+1, 0,
|
|
tview.NewTableCell(option[0]).
|
|
SetAlign(tview.AlignLeft).
|
|
SetSelectable(true).SetTextColor(style.DialogFgColor))
|
|
|
|
cmdsTable.SetCell(i+1, 1,
|
|
tview.NewTableCell(option[1]).
|
|
SetAlign(tview.AlignLeft).
|
|
SetSelectable(true).SetTextColor(style.DialogFgColor))
|
|
|
|
if len(option[0]) > col1Width {
|
|
col1Width = len(option[0])
|
|
}
|
|
|
|
if len(option[1]) > col2Width {
|
|
col2Width = len(option[1])
|
|
}
|
|
}
|
|
|
|
cmdWidth = col1Width + col2Width + 2
|
|
|
|
cmdsTable.SetFixed(1, 1)
|
|
cmdsTable.SetSelectable(true, false)
|
|
cmdsTable.SetBackgroundColor(style.DialogBgColor)
|
|
|
|
cmdLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
|
|
cmdLayout.AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false)
|
|
cmdLayout.AddItem(cmdsTable, 0, 1, true)
|
|
cmdLayout.AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false)
|
|
|
|
layout := tview.NewFlex().SetDirection(tview.FlexRow)
|
|
layout.AddItem(cmdLayout, 0, 1, true)
|
|
layout.AddItem(form, DialogFormHeight, 0, true)
|
|
layout.SetBorder(true)
|
|
layout.SetBorderColor(style.DialogBorderColor)
|
|
layout.SetBackgroundColor(style.DialogBgColor)
|
|
|
|
selectedStyle := tcell.StyleDefault.
|
|
Background(style.TableSelectedBgColor).
|
|
Foreground(style.TableSelectedFgColor)
|
|
|
|
cmdsTable.SetSelectedStyle(selectedStyle)
|
|
|
|
return &CommandDialog{
|
|
Box: tview.NewBox().SetBorder(false),
|
|
layout: layout,
|
|
table: cmdsTable,
|
|
form: form,
|
|
display: false,
|
|
options: options,
|
|
width: cmdWidth + cmdWidthOffset,
|
|
height: len(options) + TableHeightOffset + DialogFormHeight,
|
|
focusElement: cmdTableFocus,
|
|
selectedStyle: selectedStyle,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// GetSelectedItem returns selected row item.
|
|
func (cmd *CommandDialog) GetSelectedItem() string {
|
|
row, _ := cmd.table.GetSelection()
|
|
if row >= 0 {
|
|
return cmd.options[row-1][0]
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// GetCommandCount returns number of commands
|
|
func (cmd *CommandDialog) GetCommandCount() int {
|
|
return cmd.table.GetRowCount()
|
|
}
|
|
|
|
// Display this primitive.
|
|
func (cmd *CommandDialog) Display() {
|
|
cmd.table.Select(1, 0)
|
|
cmd.form.SetFocus(1)
|
|
cmd.display = true
|
|
}
|
|
|
|
// Hide this primitive
|
|
func (cmd *CommandDialog) Hide() {
|
|
cmd.display = false
|
|
cmd.focusElement = cmdTableFocus
|
|
|
|
cmd.table.SetSelectedStyle(cmd.selectedStyle)
|
|
}
|
|
|
|
// HasFocus returns whether this primitive has focus
|
|
func (cmd *CommandDialog) HasFocus() bool {
|
|
return utils.CheckFocus(cmd.table, cmd.form)
|
|
}
|
|
|
|
func (cmd *CommandDialog) IsDisplay() bool {
|
|
return cmd.display
|
|
}
|
|
|
|
func (cmd *CommandDialog) Focus(delegate func(tview.Primitive)) {
|
|
if cmd.focusElement == cmdTableFocus {
|
|
delegate(cmd.table)
|
|
|
|
return
|
|
}
|
|
|
|
button := cmd.form.GetButton(cmd.form.GetButtonCount() - 1)
|
|
|
|
button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
|
if event.Key() == utils.SwitchFocusKey.Key {
|
|
cmd.focusElement = cmdTableFocus
|
|
|
|
cmd.Focus(delegate)
|
|
cmd.form.SetFocus(0)
|
|
|
|
return nil
|
|
}
|
|
|
|
return event
|
|
})
|
|
|
|
delegate(cmd.form)
|
|
}
|
|
|
|
func (cmd *CommandDialog) InputHandler() func(event *tcell.EventKey, setFocus func(primitive tview.Primitive)) {
|
|
return cmd.WrapInputHandler(func(event *tcell.EventKey, setFocus func(primitive tview.Primitive)) {
|
|
cmd.logger.Sugar().Debugf("command dialog event %v received", event)
|
|
|
|
if event.Key() == utils.CloseDialogKey.Key {
|
|
cmd.cancelHandler()
|
|
|
|
return
|
|
}
|
|
|
|
if event.Key() == utils.SwitchFocusKey.Key {
|
|
cmd.setFocusElement()
|
|
}
|
|
|
|
if cmd.form.HasFocus() {
|
|
if formHandler := cmd.form.InputHandler(); formHandler != nil {
|
|
formHandler(event, setFocus)
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
if cmd.table.HasFocus() {
|
|
if event.Key() == tcell.KeyEnter {
|
|
cmd.selectHandler()
|
|
|
|
return
|
|
}
|
|
|
|
if tableHandler := cmd.table.InputHandler(); tableHandler != nil {
|
|
tableHandler(event, setFocus)
|
|
|
|
return
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// SetSelectedFunc sets the form enter button selected function
|
|
func (cmd *CommandDialog) SetSelectedFunc(handler func()) *CommandDialog {
|
|
cmd.selectHandler = handler
|
|
|
|
return cmd
|
|
}
|
|
|
|
// SetCancelFunc sets form cancel button selected function.
|
|
func (cmd *CommandDialog) SetCancelFunc(handler func()) *CommandDialog {
|
|
cmd.cancelHandler = handler
|
|
cancelButton := cmd.form.GetButton(cmd.form.GetButtonCount() - 1)
|
|
|
|
cancelButton.SetSelectedFunc(handler)
|
|
|
|
return cmd
|
|
}
|
|
|
|
// SetRect set rects for this primitive
|
|
func (cmd *CommandDialog) SetRect(x, y, width, height int) {
|
|
ws := (width - cmd.width) / 2
|
|
hs := (height - cmd.height) / 2
|
|
dy := y + hs
|
|
bWidth := cmd.width
|
|
|
|
if cmd.width > width {
|
|
ws = 0
|
|
bWidth = width - 1
|
|
}
|
|
|
|
bHeight := cmd.height
|
|
|
|
if cmd.height > height {
|
|
dy = y + 1
|
|
bHeight = height - 1
|
|
}
|
|
|
|
cmd.Box.SetRect(x+ws, dy, bWidth, bHeight)
|
|
|
|
x, y, width, height = cmd.Box.GetInnerRect()
|
|
|
|
cmd.layout.SetRect(x, y, width, height)
|
|
}
|
|
|
|
func (cmd *CommandDialog) Draw(screen tcell.Screen) {
|
|
if !cmd.display {
|
|
return
|
|
}
|
|
|
|
cmd.Box.DrawForSubclass(screen, cmd)
|
|
cmd.layout.Draw(screen)
|
|
}
|
|
|
|
func (cmd *CommandDialog) setFocusElement() {
|
|
if cmd.focusElement == cmdTableFocus {
|
|
cmd.focusElement = cmdFormFocus
|
|
cmd.table.SetSelectedStyle(tcell.StyleDefault.
|
|
Background(style.DialogBgColor).
|
|
Foreground(style.DialogFgColor))
|
|
} else {
|
|
cmd.focusElement = cmdTableFocus
|
|
cmd.table.SetSelectedStyle(cmd.selectedStyle)
|
|
}
|
|
}
|