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

199 lines
3.8 KiB
Go

package tui
import (
"github.com/lixenwraith/terminal"
)
// KeyValue renders right-aligned key, separator, left-aligned value on row
// Key width auto-sizes based on content, capped at 40% of region width
// Value gets remainder, minimum 30% of region width
func (r Region) KeyValue(y int, key, value string, keyStyle, valStyle Style, sep rune) {
if y < 0 || y >= r.H || r.W < 3 {
return
}
keyLen := RuneLen(key)
// Dynamic allocation: key gets what it needs up to 40%
maxKeyW := (r.W * 2) / 5 // 40%
minValW := (r.W * 3) / 10 // 30%
keyW := keyLen
if keyW > maxKeyW {
keyW = maxKeyW
}
if keyW < 1 {
keyW = 1
}
valW := r.W - keyW - 1 // -1 for separator
if valW < minValW && r.W > minValW+2 {
// Reclaim from key to meet minimum value width
valW = minValW
keyW = r.W - valW - 1
if keyW < 1 {
keyW = 1
valW = r.W - 2
}
}
if valW < 1 {
valW = 1
}
// Truncate key if needed
keyRunes := []rune(key)
if len(keyRunes) > keyW {
if keyW > 1 {
keyRunes = keyRunes[:keyW-1]
keyRunes = append(keyRunes, '…')
} else {
keyRunes = keyRunes[:1]
}
}
// Truncate value if needed
valRunes := []rune(value)
if len(valRunes) > valW {
if valW > 1 {
valRunes = valRunes[:valW-1]
valRunes = append(valRunes, '…')
} else {
valRunes = valRunes[:1]
}
}
// Right-align key within allocated width
keyX := keyW - len(keyRunes)
for i, ch := range keyRunes {
r.Cell(keyX+i, y, ch, keyStyle.Fg, keyStyle.Bg, keyStyle.Attr)
}
// Separator
r.Cell(keyW, y, sep, keyStyle.Fg, keyStyle.Bg, terminal.AttrDim)
// Left-align value
for i, ch := range valRunes {
r.Cell(keyW+1+i, y, ch, valStyle.Fg, valStyle.Bg, valStyle.Attr)
}
}
// KeyValueWrap renders key-value with value wrapping to subsequent lines
// Returns number of lines used
// Layout:
//
// key: value text that is
// long and wraps to
// next line
func (r Region) KeyValueWrap(y int, key, value string, keyStyle, valStyle Style, sep rune) int {
if y < 0 || y >= r.H || r.W < 3 {
return 0
}
keyLen := RuneLen(key)
// Dynamic allocation same as KeyValue
maxKeyW := (r.W * 2) / 5 // 40%
minValW := (r.W * 3) / 10 // 30%
keyW := keyLen
if keyW > maxKeyW {
keyW = maxKeyW
}
if keyW < 1 {
keyW = 1
}
valW := r.W - keyW - 1 // -1 for separator
if valW < minValW && r.W > minValW+2 {
valW = minValW
keyW = r.W - valW - 1
if keyW < 1 {
keyW = 1
valW = r.W - 2
}
}
if valW < 1 {
valW = 1
}
// Truncate key if needed
keyRunes := []rune(key)
if len(keyRunes) > keyW {
if keyW > 1 {
keyRunes = keyRunes[:keyW-1]
keyRunes = append(keyRunes, '…')
} else {
keyRunes = keyRunes[:1]
}
}
// Right-align key within allocated width
keyX := keyW - len(keyRunes)
for i, ch := range keyRunes {
r.Cell(keyX+i, y, ch, keyStyle.Fg, keyStyle.Bg, keyStyle.Attr)
}
// Separator
r.Cell(keyW, y, sep, keyStyle.Fg, keyStyle.Bg, terminal.AttrDim)
// Wrap value text
valueX := keyW + 1
lines := WrapText(value, valW)
if len(lines) == 0 {
return 1
}
rendered := 0
for i, line := range lines {
lineY := y + i
if lineY >= r.H {
break
}
r.Text(valueX, lineY, line, valStyle.Fg, valStyle.Bg, valStyle.Attr)
rendered++
}
if rendered < 1 {
rendered = 1
}
return rendered
}
// MeasureKeyValueWrap calculates lines needed for KeyValueWrap without rendering
// Useful for layout pre-calculation
func (r Region) MeasureKeyValueWrap(key, value string) int {
if r.W < 3 {
return 1
}
keyLen := RuneLen(key)
maxKeyW := (r.W * 2) / 5
minValW := (r.W * 3) / 10
keyW := keyLen
if keyW > maxKeyW {
keyW = maxKeyW
}
if keyW < 1 {
keyW = 1
}
valW := r.W - keyW - 1
if valW < minValW && r.W > minValW+2 {
valW = minValW
keyW = r.W - valW - 1
if keyW < 1 {
keyW = 1
valW = r.W - 2
}
}
if valW < 1 {
valW = 1
}
lines := WrapText(value, valW)
if len(lines) == 0 {
return 1
}
return len(lines)
}