1package view 2 3import ( 4 "fmt" 5 "strings" 6 7 "github.com/gdamore/tcell" 8 "github.com/rivo/tview" 9 "github.com/wtfutil/wtf/cfg" 10 "github.com/wtfutil/wtf/wtf" 11) 12 13type helpItem struct { 14 Key string 15 Text string 16} 17 18// KeyboardWidget manages keyboard control for a widget 19type KeyboardWidget struct { 20 app *tview.Application 21 pages *tview.Pages 22 view *tview.TextView 23 settings *cfg.Common 24 25 charMap map[string]func() 26 keyMap map[tcell.Key]func() 27 charHelp []helpItem 28 keyHelp []helpItem 29 maxKey int 30} 31 32// NewKeyboardWidget creates and returns a new instance of KeyboardWidget 33func NewKeyboardWidget(app *tview.Application, pages *tview.Pages, settings *cfg.Common) KeyboardWidget { 34 keyWidget := KeyboardWidget{ 35 app: app, 36 pages: pages, 37 settings: settings, 38 charMap: make(map[string]func()), 39 keyMap: make(map[tcell.Key]func()), 40 charHelp: []helpItem{}, 41 keyHelp: []helpItem{}, 42 } 43 44 return keyWidget 45} 46 47// SetKeyboardChar sets a character/function combination that responds to key presses 48// Example: 49// 50// widget.SetKeyboardChar("d", widget.deleteSelectedItem) 51// 52func (widget *KeyboardWidget) SetKeyboardChar(char string, fn func(), helpText string) { 53 if char == "" { 54 return 55 } 56 57 widget.charMap[char] = fn 58 widget.charHelp = append(widget.charHelp, helpItem{char, helpText}) 59} 60 61// SetKeyboardKey sets a tcell.Key/function combination that responds to key presses 62// Example: 63// 64// widget.SetKeyboardKey(tcell.KeyCtrlD, widget.deleteSelectedItem) 65// 66func (widget *KeyboardWidget) SetKeyboardKey(key tcell.Key, fn func(), helpText string) { 67 widget.keyMap[key] = fn 68 widget.keyHelp = append(widget.keyHelp, helpItem{tcell.KeyNames[key], helpText}) 69 70 if len(tcell.KeyNames[key]) > widget.maxKey { 71 widget.maxKey = len(tcell.KeyNames[key]) 72 } 73} 74 75// InitializeCommonControls sets up the keyboard controls that are common to 76// all widgets that accept keyboard input 77func (widget *KeyboardWidget) InitializeCommonControls(refreshFunc func()) { 78 widget.SetKeyboardChar("/", widget.ShowHelp, "Show/hide this help prompt") 79 80 if refreshFunc != nil { 81 widget.SetKeyboardChar("r", refreshFunc, "Refresh widget") 82 } 83} 84 85// InputCapture is the function passed to tview's SetInputCapture() function 86// This is done during the main widget's creation process using the following code: 87// 88// widget.View.SetInputCapture(widget.InputCapture) 89// 90func (widget *KeyboardWidget) InputCapture(event *tcell.EventKey) *tcell.EventKey { 91 if event == nil { 92 return nil 93 } 94 95 fn := widget.charMap[string(event.Rune())] 96 if fn != nil { 97 fn() 98 return nil 99 } 100 101 fn = widget.keyMap[event.Key()] 102 if fn != nil { 103 fn() 104 return nil 105 } 106 107 return event 108} 109 110// HelpText returns the help text and keyboard command info for this widget 111func (widget *KeyboardWidget) HelpText() string { 112 str := " [green::b]Keyboard commands for " + strings.Title(widget.settings.Module.Type) + "[white]\n\n" 113 114 for _, item := range widget.charHelp { 115 str += fmt.Sprintf(" %s\t%s\n", item.Key, item.Text) 116 } 117 str += "\n\n" 118 119 for _, item := range widget.keyHelp { 120 str += fmt.Sprintf(" %-*s\t%s\n", widget.maxKey, item.Key, item.Text) 121 } 122 123 return str 124} 125 126func (widget *KeyboardWidget) SetView(view *tview.TextView) { 127 widget.view = view 128} 129 130func (widget *KeyboardWidget) ShowHelp() { 131 closeFunc := func() { 132 widget.pages.RemovePage("help") 133 widget.app.SetFocus(widget.view) 134 } 135 136 modal := wtf.NewBillboardModal(widget.HelpText(), closeFunc) 137 138 widget.pages.AddPage("help", modal, false, true) 139 widget.app.SetFocus(modal) 140 141 widget.app.QueueUpdate(func() { 142 widget.app.Draw() 143 }) 144} 145