table.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
  2. // Use of this source code is governed by a MIT license that can
  3. // be found in the LICENSE file.
  4. package termui
  5. import "strings"
  6. /* Table is like:
  7. ┌Awesome Table ────────────────────────────────────────────────┐
  8. │ Col0 | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
  9. │──────────────────────────────────────────────────────────────│
  10. │ Some Item #1 | AAA | 123 | CCCCC | EEEEE | GGGGG | IIIII |
  11. │──────────────────────────────────────────────────────────────│
  12. │ Some Item #2 | BBB | 456 | DDDDD | FFFFF | HHHHH | JJJJJ |
  13. └──────────────────────────────────────────────────────────────┘
  14. Datapoints are a two dimensional array of strings: [][]string
  15. Example:
  16. data := [][]string{
  17. {"Col0", "Col1", "Col3", "Col4", "Col5", "Col6"},
  18. {"Some Item #1", "AAA", "123", "CCCCC", "EEEEE", "GGGGG", "IIIII"},
  19. {"Some Item #2", "BBB", "456", "DDDDD", "FFFFF", "HHHHH", "JJJJJ"},
  20. }
  21. table := termui.NewTable()
  22. table.Rows = data // type [][]string
  23. table.FgColor = termui.ColorWhite
  24. table.BgColor = termui.ColorDefault
  25. table.Height = 7
  26. table.Width = 62
  27. table.Y = 0
  28. table.X = 0
  29. table.Border = true
  30. */
  31. // Table tracks all the attributes of a Table instance
  32. type Table struct {
  33. Block
  34. Rows [][]string
  35. CellWidth []int
  36. FgColor Attribute
  37. BgColor Attribute
  38. FgColors []Attribute
  39. BgColors []Attribute
  40. Separator bool
  41. TextAlign Align
  42. }
  43. // NewTable returns a new Table instance
  44. func NewTable() *Table {
  45. table := &Table{Block: *NewBlock()}
  46. table.FgColor = ColorWhite
  47. table.BgColor = ColorDefault
  48. table.Separator = true
  49. return table
  50. }
  51. // CellsWidth calculates the width of a cell array and returns an int
  52. func cellsWidth(cells []Cell) int {
  53. width := 0
  54. for _, c := range cells {
  55. width += c.Width()
  56. }
  57. return width
  58. }
  59. // Analysis generates and returns an array of []Cell that represent all columns in the Table
  60. func (table *Table) Analysis() [][]Cell {
  61. var rowCells [][]Cell
  62. length := len(table.Rows)
  63. if length < 1 {
  64. return rowCells
  65. }
  66. if len(table.FgColors) == 0 {
  67. table.FgColors = make([]Attribute, len(table.Rows))
  68. }
  69. if len(table.BgColors) == 0 {
  70. table.BgColors = make([]Attribute, len(table.Rows))
  71. }
  72. cellWidths := make([]int, len(table.Rows[0]))
  73. for y, row := range table.Rows {
  74. if table.FgColors[y] == 0 {
  75. table.FgColors[y] = table.FgColor
  76. }
  77. if table.BgColors[y] == 0 {
  78. table.BgColors[y] = table.BgColor
  79. }
  80. for x, str := range row {
  81. cells := DefaultTxBuilder.Build(str, table.FgColors[y], table.BgColors[y])
  82. cw := cellsWidth(cells)
  83. if cellWidths[x] < cw {
  84. cellWidths[x] = cw
  85. }
  86. rowCells = append(rowCells, cells)
  87. }
  88. }
  89. table.CellWidth = cellWidths
  90. return rowCells
  91. }
  92. // SetSize calculates the table size and sets the internal value
  93. func (table *Table) SetSize() {
  94. length := len(table.Rows)
  95. if table.Separator {
  96. table.Height = length*2 + 1
  97. } else {
  98. table.Height = length + 2
  99. }
  100. table.Width = 2
  101. if length != 0 {
  102. for _, cellWidth := range table.CellWidth {
  103. table.Width += cellWidth + 3
  104. }
  105. }
  106. }
  107. // CalculatePosition ...
  108. func (table *Table) CalculatePosition(x int, y int, coordinateX *int, coordinateY *int, cellStart *int) {
  109. if table.Separator {
  110. *coordinateY = table.innerArea.Min.Y + y*2
  111. } else {
  112. *coordinateY = table.innerArea.Min.Y + y
  113. }
  114. if x == 0 {
  115. *cellStart = table.innerArea.Min.X
  116. } else {
  117. *cellStart += table.CellWidth[x-1] + 3
  118. }
  119. switch table.TextAlign {
  120. case AlignRight:
  121. *coordinateX = *cellStart + (table.CellWidth[x] - len(table.Rows[y][x])) + 2
  122. case AlignCenter:
  123. *coordinateX = *cellStart + (table.CellWidth[x]-len(table.Rows[y][x]))/2 + 2
  124. default:
  125. *coordinateX = *cellStart + 2
  126. }
  127. }
  128. // Buffer ...
  129. func (table *Table) Buffer() Buffer {
  130. buffer := table.Block.Buffer()
  131. rowCells := table.Analysis()
  132. pointerX := table.innerArea.Min.X + 2
  133. pointerY := table.innerArea.Min.Y
  134. borderPointerX := table.innerArea.Min.X
  135. for y, row := range table.Rows {
  136. for x := range row {
  137. table.CalculatePosition(x, y, &pointerX, &pointerY, &borderPointerX)
  138. background := DefaultTxBuilder.Build(strings.Repeat(" ", table.CellWidth[x]+3), table.BgColors[y], table.BgColors[y])
  139. cells := rowCells[y*len(row)+x]
  140. for i, back := range background {
  141. buffer.Set(borderPointerX+i, pointerY, back)
  142. }
  143. coordinateX := pointerX
  144. for _, printer := range cells {
  145. buffer.Set(coordinateX, pointerY, printer)
  146. coordinateX += printer.Width()
  147. }
  148. if x != 0 {
  149. dividors := DefaultTxBuilder.Build("|", table.FgColors[y], table.BgColors[y])
  150. for _, dividor := range dividors {
  151. buffer.Set(borderPointerX, pointerY, dividor)
  152. }
  153. }
  154. }
  155. if table.Separator {
  156. border := DefaultTxBuilder.Build(strings.Repeat("─", table.Width-2), table.FgColor, table.BgColor)
  157. for i, cell := range border {
  158. buffer.Set(i+1, pointerY+1, cell)
  159. }
  160. }
  161. }
  162. return buffer
  163. }