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