v0.1.4 tui uniform transparent background policy, tui example added

This commit is contained in:
2026-07-22 10:05:08 -04:00
parent 056401d20a
commit 228e8ac1ad
6 changed files with 1738 additions and 10 deletions
+2 -5
View File
@@ -28,9 +28,7 @@ func (r Region) Pane(opts PaneOpts) Region {
r.Box(opts.Border, opts.BorderFg)
// Title on top edge
headerH := 0
if opts.Title != "" {
headerH = 1
title := " " + opts.Title + " "
if RuneLen(title) > r.W-4 {
title = Truncate(title, r.W-4)
@@ -44,8 +42,8 @@ func (r Region) Pane(opts PaneOpts) Region {
}
}
// Return content region (inside border, below title)
return r.Sub(1, 1+headerH, r.W-2, r.H-2-headerH)
// Content region is everything inside the border. The title sits ON the border row.
return r.Sub(1, 1, r.W-2, r.H-2)
}
// TitledPane fills region with background, draws centered title at top, returns content region
@@ -68,4 +66,3 @@ func (r Region) TitledPaneFocused(title string, titleFg, bg, focusBg color.RGB,
}
return r.TitledPane(title, titleFg, bg)
}
+31 -4
View File
@@ -65,7 +65,11 @@ func (r Region) Inset(n int) Region {
return r.Sub(n, n, r.W-2*n, r.H-2*n)
}
// Cell sets a single cell with bounds checking
// Cell sets a single cell with bounds checking.
// A zero-value bg (color.RGB{}) is transparent: the existing cell's
// background is preserved. Establish a base background first (Fill, or
// pre-initialized cell buffer). For a literal black background use
// CellOpaque or Fill(color.RGB{}).
func (r Region) Cell(x, y int, ch rune, fg, bg color.RGB, attr terminal.Attr) {
if x < 0 || x >= r.W || y < 0 || y >= r.H {
return
@@ -81,15 +85,39 @@ func (r Region) Cell(x, y int, ch rune, fg, bg color.RGB, attr terminal.Attr) {
idx := absY*r.TotalW + absX
// Single bounds check for the backing slice
if uint(idx) < uint(len(r.Cells)) {
if bg == (color.RGB{}) {
bg = r.Cells[idx].Bg // transparent: inherit current background
}
r.Cells[idx] = terminal.Cell{Rune: ch, Fg: fg, Bg: bg, Attrs: attr}
}
}
// Fill fills entire region with background color
// CellOpaque sets a single cell writing bg verbatim, including zero (black).
// Used by base-layer operations (Fill) that must be able to write any color.
func (r Region) CellOpaque(x, y int, ch rune, fg, bg color.RGB, attr terminal.Attr) {
if x < 0 || x >= r.W || y < 0 || y >= r.H {
return
}
absX := r.X + x
absY := r.Y + y
if uint(absX) >= uint(r.TotalW) {
return
}
idx := absY*r.TotalW + absX
if uint(idx) < uint(len(r.Cells)) {
r.Cells[idx] = terminal.Cell{Rune: ch, Fg: fg, Bg: bg, Attrs: attr}
}
}
// Fill fills entire region with background color.
// Opaque by definition: Fill establishes the base layer, so bg is written
// verbatim (including zero/black) rather than treated as transparent.
func (r Region) Fill(bg color.RGB) {
for y := 0; y < r.H; y++ {
for x := 0; x < r.W; x++ {
r.Cell(x, y, ' ', color.RGB{}, bg, terminal.AttrNone)
r.CellOpaque(x, y, ' ', color.RGB{}, bg, terminal.AttrNone)
}
}
}
@@ -113,4 +141,3 @@ func (r Region) Height() int {
func (r Region) Bounds() (x, y, w, h int) {
return r.X, r.Y, r.W, r.H
}
+10 -1
View File
@@ -129,7 +129,15 @@ func (r Region) Table(headers []string, rows [][]string, opts TableOpts) {
}
// renderTableRow renders a single table row
// The row is cleared to style.Bg across the full region width first, so
// alternating-row backgrounds render as continuous bands rather than only
// under glyphs. A zero style.Bg inherits the underlying background.
func (r Region) renderTableRow(y int, cells []string, widths []int, aligns []Align, sep rune, style Style) {
// Clear row with row background (full width, List-consistent banding)
for cx := 0; cx < r.W; cx++ {
r.Cell(cx, y, ' ', style.Fg, style.Bg, terminal.AttrNone)
}
x := 0
for i, w := range widths {
if x >= r.W {
@@ -177,4 +185,5 @@ func (r Region) renderTableRow(y int, cells []string, widths []int, aligns []Ali
x++
}
}
}
}