aboutsummaryrefslogtreecommitdiff
path: root/internal/game/ui_table.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/ui_table.go')
-rw-r--r--internal/game/ui_table.go150
1 files changed, 150 insertions, 0 deletions
diff --git a/internal/game/ui_table.go b/internal/game/ui_table.go
new file mode 100644
index 0000000..b96dca1
--- /dev/null
+++ b/internal/game/ui_table.go
@@ -0,0 +1,150 @@
+package game
+
+import (
+ "strings"
+
+ "thehouseoficarus/internal/color"
+)
+
+type tableGlyphs struct {
+ topLeft, topSep, topRight rune
+ side, colSep rune
+ sepLeft, sepCross, sepRight rune
+ botLeft, botSep, botRight rune
+ fillH, fillHSep rune
+ titleSep rune
+}
+
+func tableGlyphSet(unicode bool) tableGlyphs {
+ if unicode {
+ return tableGlyphs{
+ topLeft: '╔', topSep: '╤', topRight: '╗', side: '║', colSep: '│',
+ sepLeft: '╟', sepCross: '┼', sepRight: '╢',
+ botLeft: '╚', botSep: '╧', botRight: '╝', fillH: '═', fillHSep: '─',
+ titleSep: '┬',
+ }
+ }
+ return tableGlyphs{
+ topLeft: '+', topSep: '+', topRight: '+', side: '|', colSep: '|',
+ sepLeft: '+', sepCross: '+', sepRight: '+',
+ botLeft: '+', botSep: '+', botRight: '+', fillH: '-', fillHSep: '-',
+ titleSep: '+',
+ }
+}
+
+type Table struct {
+ Title string
+ Columns []string
+ Rows [][]string
+}
+
+func (t *Table) Render(unicode bool) []string {
+ g := tableGlyphSet(unicode)
+
+ nCols := len(t.Columns)
+ if nCols == 0 && len(t.Rows) > 0 {
+ nCols = len(t.Rows[0])
+ }
+ if nCols == 0 {
+ return nil
+ }
+
+ colWidths := make([]int, nCols)
+ for i, h := range t.Columns {
+ colWidths[i] = color.VisibleLen(h)
+ }
+ for _, row := range t.Rows {
+ for i, cell := range row {
+ if i >= nCols {
+ break
+ }
+ vl := color.VisibleLen(cell)
+ if vl > colWidths[i] {
+ colWidths[i] = vl
+ }
+ }
+ }
+
+ makeSep := func(left, cross, right rune, fill rune) string {
+ var b strings.Builder
+ b.WriteRune(left)
+ for i := 0; i < nCols; i++ {
+ if i > 0 {
+ b.WriteRune(cross)
+ }
+ b.WriteString(strings.Repeat(string(fill), colWidths[i]+2))
+ }
+ b.WriteRune(right)
+ return b.String()
+ }
+
+ makeRow := func(cells []string) string {
+ var b strings.Builder
+ b.WriteRune(g.side)
+ for i := 0; i < nCols; i++ {
+ if i > 0 {
+ b.WriteRune(g.colSep)
+ }
+ cell := ""
+ if i < len(cells) {
+ cell = cells[i]
+ }
+ pad := colWidths[i] - color.VisibleLen(cell)
+ if pad < 0 {
+ pad = 0
+ }
+ b.WriteString(" " + cell + strings.Repeat(" ", pad) + " ")
+ }
+ b.WriteRune(g.side)
+ return b.String()
+ }
+
+ hasTitle := t.Title != ""
+ hasCols := len(t.Columns) > 0
+
+ var out []string
+
+ if hasTitle {
+ innerWidth := 0
+ for _, w := range colWidths {
+ innerWidth += w + 2
+ }
+ innerWidth += nCols - 1
+
+ var tb strings.Builder
+ tb.WriteRune(g.topLeft)
+ tb.WriteString(strings.Repeat(string(g.fillH), innerWidth))
+ tb.WriteRune(g.topRight)
+ out = append(out, tb.String())
+
+ var tr strings.Builder
+ tr.WriteRune(g.side)
+ tr.WriteString(" ")
+ tr.WriteString(t.Title)
+ padding := innerWidth - 2 - color.VisibleLen(t.Title)
+ if padding < 0 {
+ padding = 0
+ }
+ tr.WriteString(strings.Repeat(" ", padding))
+ tr.WriteString(" ")
+ tr.WriteRune(g.side)
+ out = append(out, tr.String())
+
+ out = append(out, makeSep(g.sepLeft, g.titleSep, g.sepRight, g.fillHSep))
+ } else {
+ out = append(out, makeSep(g.topLeft, g.topSep, g.topRight, g.fillH))
+ }
+
+ if hasCols {
+ out = append(out, makeRow(t.Columns))
+ out = append(out, makeSep(g.sepLeft, g.sepCross, g.sepRight, g.fillHSep))
+ }
+
+ for _, row := range t.Rows {
+ out = append(out, makeRow(row))
+ }
+
+ out = append(out, makeSep(g.botLeft, g.botSep, g.botRight, g.fillH))
+
+ return out
+}