Files
terminal/tui/tab.go
T
2026-07-12 18:41:05 -04:00

131 lines
2.8 KiB
Go

package tui
import "github.com/lixenwraith/terminal"
// TabBounds stores position and size of a rendered tab
type TabBounds struct {
X, W int
}
// TabBarOpts configures tab bar rendering
type TabBarOpts struct {
ActiveStyle Style
InactiveStyle Style
Separator string // Between tabs, default " │ "
Padding int // Horizontal padding inside each tab, default 1
}
// DefaultTabBarOpts returns sensible defaults
func DefaultTabBarOpts() TabBarOpts {
return TabBarOpts{
ActiveStyle: Style{Attr: terminal.AttrBold | terminal.AttrReverse},
InactiveStyle: Style{Attr: terminal.AttrNone},
Separator: " │ ",
Padding: 1,
}
}
// TabBar renders horizontal tab strip at row y
// Returns bounds of each tab for hit testing / navigation
func (r Region) TabBar(y int, titles []string, active int, opts TabBarOpts) []TabBounds {
if y < 0 || y >= r.H || len(titles) == 0 {
return nil
}
if opts.Separator == "" {
opts.Separator = " │ "
}
bounds := make([]TabBounds, len(titles))
x := 0
sepLen := RuneLen(opts.Separator)
for i, title := range titles {
if x >= r.W {
break
}
// Calculate tab width
tabW := RuneLen(title) + opts.Padding*2
if x+tabW > r.W {
tabW = r.W - x
}
bounds[i] = TabBounds{X: x, W: tabW}
// Select style
style := opts.InactiveStyle
if i == active {
style = opts.ActiveStyle
}
// Render padding + title + padding
for j := 0; j < opts.Padding && x+j < r.W; j++ {
r.Cell(x+j, y, ' ', style.Fg, style.Bg, style.Attr)
}
titleStart := x + opts.Padding
for j, ch := range title {
if titleStart+j >= r.W {
break
}
r.Cell(titleStart+j, y, ch, style.Fg, style.Bg, style.Attr)
}
for j := 0; j < opts.Padding; j++ {
pos := x + opts.Padding + RuneLen(title) + j
if pos < r.W {
r.Cell(pos, y, ' ', style.Fg, style.Bg, style.Attr)
}
}
x += tabW
// Separator between tabs
if i < len(titles)-1 && x+sepLen <= r.W {
for j, ch := range opts.Separator {
r.Cell(x+j, y, ch, opts.InactiveStyle.Fg, opts.InactiveStyle.Bg, terminal.AttrDim)
}
x += sepLen
}
}
return bounds
}
// TabBarCentered renders tab bar centered horizontally
func (r Region) TabBarCentered(y int, titles []string, active int, opts TabBarOpts) []TabBounds {
if len(titles) == 0 {
return nil
}
if opts.Separator == "" {
opts.Separator = " │ "
}
// Calculate total width
totalW := 0
sepLen := RuneLen(opts.Separator)
for i, title := range titles {
totalW += RuneLen(title) + opts.Padding*2
if i < len(titles)-1 {
totalW += sepLen
}
}
// Create sub-region for centering
startX := (r.W - totalW) / 2
if startX < 0 {
startX = 0
}
subR := r.Sub(startX, y, totalW, 1)
bounds := subR.TabBar(0, titles, active, opts)
// Adjust bounds to parent coordinates
for i := range bounds {
bounds[i].X += startX
}
return bounds
}