Config view: one list with section headings, navigated with the arrow keys
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+90
-8
@@ -50,16 +50,19 @@ func arrowNavigation(f *tview.Form, cancel func(), hooks *browseHooks) {
|
||||
pos = f.GetFormItemCount() + button
|
||||
}
|
||||
switch ev.Key() {
|
||||
case tcell.KeyDown:
|
||||
if pos == total-1 && hooks != nil && hooks.edge != nil && hooks.edge(true) {
|
||||
case tcell.KeyDown, tcell.KeyUp:
|
||||
dir := 1
|
||||
if ev.Key() == tcell.KeyUp {
|
||||
dir = -1
|
||||
}
|
||||
next := neighbour(f, pos, dir)
|
||||
if next == pos { // nothing further in this direction
|
||||
if hooks != nil && hooks.edge != nil && hooks.edge(dir > 0) {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
f.SetFocus(min(pos+1, total-1))
|
||||
case tcell.KeyUp:
|
||||
if pos == 0 && hooks != nil && hooks.edge != nil && hooks.edge(false) {
|
||||
return nil
|
||||
}
|
||||
f.SetFocus(max(pos-1, 0))
|
||||
f.SetFocus(next)
|
||||
case tcell.KeyTab, tcell.KeyBacktab:
|
||||
dir := 1
|
||||
if ev.Key() == tcell.KeyBacktab {
|
||||
@@ -180,3 +183,82 @@ func (b *browser) track(dd *tview.DropDown, ev *tcell.EventKey, h *browseHooks)
|
||||
b.known = false // paging, typed prefixes
|
||||
}
|
||||
}
|
||||
|
||||
// skipsFocus marks form items that only decorate (section headings): the
|
||||
// arrow keys pass over them.
|
||||
type skipsFocus interface{ skipFocus() bool }
|
||||
|
||||
// neighbour returns the position of the next focusable item or button after
|
||||
// pos in the given direction, or pos itself if there is none.
|
||||
func neighbour(f *tview.Form, pos, dir int) int {
|
||||
total := f.GetFormItemCount() + f.GetButtonCount()
|
||||
for i := pos + dir; i >= 0 && i < total; i += dir {
|
||||
if i < f.GetFormItemCount() {
|
||||
if s, ok := f.GetFormItem(i).(skipsFocus); ok && s.skipFocus() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
// firstFocusable returns the position of the first item or button that can
|
||||
// take the focus.
|
||||
func firstFocusable(f *tview.Form) int {
|
||||
if f.GetFormItemCount() == 0 {
|
||||
return 0 // a form of buttons only
|
||||
}
|
||||
if s, ok := f.GetFormItem(0).(skipsFocus); ok && s.skipFocus() {
|
||||
return neighbour(f, 0, 1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// formHeading is a section heading inside a form: a bold title above the next
|
||||
// group of fields. It never takes the focus.
|
||||
type formHeading struct {
|
||||
*tview.Box
|
||||
title string
|
||||
finished func(tcell.Key)
|
||||
}
|
||||
|
||||
func newFormHeading(title string) *formHeading {
|
||||
return &formHeading{Box: tview.NewBox(), title: title}
|
||||
}
|
||||
|
||||
func (h *formHeading) skipFocus() bool { return true }
|
||||
|
||||
func (h *formHeading) GetLabel() string { return "" }
|
||||
|
||||
func (h *formHeading) SetFormAttributes(int, tcell.Color, tcell.Color, tcell.Color, tcell.Color) tview.FormItem {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *formHeading) GetFieldWidth() int { return 0 }
|
||||
|
||||
// GetFieldHeight is 2: an empty line above the title separates the sections.
|
||||
func (h *formHeading) GetFieldHeight() int { return 2 }
|
||||
|
||||
func (h *formHeading) SetFinishedFunc(handler func(key tcell.Key)) tview.FormItem {
|
||||
h.finished = handler
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *formHeading) SetDisabled(bool) tview.FormItem { return h }
|
||||
|
||||
// Focus passes the focus on, like a disabled input field does, so tview's own
|
||||
// Tab handling skips the heading too.
|
||||
func (h *formHeading) Focus(delegate func(p tview.Primitive)) {
|
||||
if h.finished != nil {
|
||||
h.finished(-1)
|
||||
return
|
||||
}
|
||||
h.Box.Focus(delegate)
|
||||
}
|
||||
|
||||
func (h *formHeading) Draw(screen tcell.Screen) {
|
||||
h.Box.DrawForSubclass(screen, h)
|
||||
x, y, w, _ := h.GetInnerRect()
|
||||
tview.Print(screen, "[::b]"+tview.Escape(h.title)+"[::-]", x, y+1, w, tview.AlignLeft, tcell.ColorYellow)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user