199 lines
3.8 KiB
Go
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)
|
|
} |