Add basic terminal user interface

This commit is contained in:
Olli 2025-10-16 07:59:16 +02:00
commit 5956331a28
9 changed files with 878 additions and 0 deletions

163
tui/tabs.go Normal file
View file

@ -0,0 +1,163 @@
package tui
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
type TabPrimitive interface {
tview.Primitive
ForceRedraw()
}
type Tabs struct {
*tview.Flex
names []string
items map[string]tview.Primitive
selector *tview.TextView
pages *tview.Pages
fallover bool
unlock bool
}
func NewTabs(selectorAtTop bool) *Tabs {
widget := &Tabs{
Flex: tview.NewFlex(),
names: []string{},
items: make(map[string]tview.Primitive),
fallover: true,
}
widget.SetDirection(tview.FlexRow)
// Selector
widget.selector = tview.NewTextView().
SetDynamicColors(true).
SetRegions(true).
SetWrap(false).
SetTextAlign(tview.AlignLeft)
widget.selector.SetHighlightedFunc(
func(added, removed, remaining []string) {
if len(added) == 0 {
return
}
widget.pages.SwitchToPage(added[0])
},
)
if selectorAtTop {
widget.AddItem(widget.selector, 1, 0, false)
}
// Pages
widget.pages = tview.NewPages()
widget.AddItem(widget.pages, 0, 1, false)
if !selectorAtTop {
widget.AddItem(widget.selector, 1, 0, false)
}
return widget
}
func (widget *Tabs) AllowFallover(allow bool) {
widget.fallover = allow
}
func (widget *Tabs) AddText(text string) {
fmt.Fprint(widget.selector, text)
}
func (widget *Tabs) AddTab(name string, label string, item tview.Primitive, visible bool) {
widget.names = append(widget.names, name)
widget.items[name] = item
fmt.Fprintf(widget.selector, `["%s"][darkcyan]%s[white][""] `, name, label)
widget.pages.AddPage(name, item, true, false)
if visible {
widget.Select(name)
}
}
func (widget *Tabs) Select(name string) {
widget.selector.Highlight(name)
}
func (widget *Tabs) Next() tview.Primitive {
widget.unlock = true
if len(widget.names) == 0 {
return nil
}
index := -1
highlights := widget.selector.GetHighlights()
if len(highlights) > 0 {
currentName := highlights[0]
for currentIndex, name := range widget.names {
if name == currentName {
index = currentIndex
break
}
}
}
index++
if index >= len(widget.names) {
if widget.fallover {
index = 0
} else {
index = len(widget.names) - 1
}
}
widget.selector.Highlight(widget.names[index])
return widget.items[widget.names[index]]
}
func (widget *Tabs) Prev() tview.Primitive {
widget.unlock = true
if len(widget.names) == 0 {
return nil
}
index := len(widget.names)
highlights := widget.selector.GetHighlights()
if len(highlights) > 0 {
currentName := highlights[0]
for currentIndex, name := range widget.names {
if name == currentName {
index = currentIndex
break
}
}
}
index--
if index < 0 {
if widget.fallover {
index = len(widget.names) - 1
} else {
index = 0
}
}
widget.selector.Highlight(widget.names[index])
return widget.items[widget.names[index]]
}
func (widget *Tabs) Focus(delegate func(p tview.Primitive)) {
delegate(widget.pages)
}
func (widget *Tabs) Draw(screen tcell.Screen) {
if widget.unlock {
widgetX, widgetY, widgetWidth, widgetHeight := widget.GetInnerRect()
screen.LockRegion(widgetX, widgetY, widgetWidth, widgetHeight, false)
widget.unlock = false
}
widget.Flex.Draw(screen)
}