93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
package ui
|
|
|
|
import (
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
// arrowNavigation lets a form be driven with the arrow keys:
|
|
// - Up/Down move between the fields and buttons,
|
|
// - Left/Right change the value of a closed dropdown without opening it
|
|
// (and move between the buttons),
|
|
//
|
|
// Enter or Space still opens a dropdown list. While a list is open it gets all keys.
|
|
func arrowNavigation(f *tview.Form) {
|
|
f.SetInputCapture(func(ev *tcell.EventKey) *tcell.EventKey {
|
|
item, button := f.GetFocusedItemIndex()
|
|
if item < 0 && button < 0 {
|
|
return ev
|
|
}
|
|
dd, _ := focusedDropDown(f, item)
|
|
if dd != nil && dd.IsOpen() {
|
|
return ev
|
|
}
|
|
total := f.GetFormItemCount() + f.GetButtonCount()
|
|
pos := item
|
|
if button >= 0 {
|
|
pos = f.GetFormItemCount() + button
|
|
}
|
|
switch ev.Key() {
|
|
case tcell.KeyDown:
|
|
f.SetFocus(min(pos+1, total-1))
|
|
case tcell.KeyUp:
|
|
f.SetFocus(max(pos-1, 0))
|
|
case tcell.KeyLeft, tcell.KeyRight:
|
|
step := 1
|
|
if ev.Key() == tcell.KeyLeft {
|
|
step = -1
|
|
}
|
|
switch {
|
|
case dd != nil:
|
|
n := dd.GetOptionCount()
|
|
cur, _ := dd.GetCurrentOption()
|
|
dd.SetCurrentOption((cur + step + n) % n)
|
|
case button >= 0:
|
|
f.SetFocus(min(max(pos+step, f.GetFormItemCount()), total-1))
|
|
default:
|
|
return ev
|
|
}
|
|
default:
|
|
return ev
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func focusedDropDown(f *tview.Form, item int) (*tview.DropDown, bool) {
|
|
if item < 0 {
|
|
return nil, false
|
|
}
|
|
dd, ok := f.GetFormItem(item).(*tview.DropDown)
|
|
return dd, ok
|
|
}
|
|
|
|
// bindSearchNow keeps a "Search now" checkbox consistent with whether a search
|
|
// can find anything. While searchable() is false the box is unchecked, greyed
|
|
// out and cannot be ticked (blocked is called instead); it returns to the
|
|
// user's earlier choice afterwards. The returned function must be called
|
|
// whenever the answer of searchable() may have changed.
|
|
//
|
|
// tview's SetDisabled is avoided on purpose: on a form item it re-triggers
|
|
// the form's focus handling and leaves two fields highlighted.
|
|
func bindSearchNow(box *tview.Checkbox, searchable func() bool, blocked func()) (sync func()) {
|
|
want := true
|
|
box.SetChangedFunc(func(checked bool) {
|
|
switch {
|
|
case searchable():
|
|
want = checked
|
|
case checked:
|
|
box.SetChecked(false)
|
|
blocked()
|
|
}
|
|
})
|
|
return func() {
|
|
if searchable() {
|
|
box.SetLabelColor(tview.Styles.SecondaryTextColor)
|
|
box.SetChecked(want)
|
|
return
|
|
}
|
|
box.SetLabelColor(tcell.ColorGray)
|
|
box.SetChecked(false)
|
|
}
|
|
}
|