You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

300 lines
7.0 KiB

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)
}
}