table.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gui
  5. import (
  6. "fmt"
  7. "github.com/g3n/engine/math32"
  8. )
  9. //
  10. // Table implements a panel which can contains child panels
  11. // organized in rows and columns.
  12. //
  13. type Table struct {
  14. Panel // Embedded panel
  15. styles *TableStyles // pointer to current styles
  16. cols []TableColumn // array of columns descriptors
  17. colmap map[string]*TableColumn // maps column id to column descriptor
  18. firstRow int // index of the first row of data to show
  19. rows []tableRow // array of table rows
  20. headerHeight float32
  21. }
  22. // TableColumn describes a table column
  23. type TableColumn struct {
  24. Id string // column id used to reference the column
  25. Name string // column name shown in the header
  26. Width float32 // column preferable width in pixels
  27. Hidden bool // hidden flag
  28. Format string // format string for numbers and strings
  29. Align int // cell align
  30. order int // show order
  31. header *Panel // header panel
  32. label *Label // header label
  33. }
  34. // TableHeaderStyle describes the style of the table header
  35. type TableHeaderStyle struct {
  36. Border BorderSizes
  37. Paddings BorderSizes
  38. BorderColor math32.Color4
  39. BgColor math32.Color
  40. FgColor math32.Color
  41. }
  42. // TableRowStyle describes the style of the table row
  43. type TableRowStyle struct {
  44. Border BorderSizes
  45. Paddings BorderSizes
  46. BorderColor math32.Color4
  47. BgColor math32.Color
  48. FgColor math32.Color
  49. }
  50. // TableRowStyles describes all styles for the table row
  51. type TableRowStyles struct {
  52. Normal TableRowStyle
  53. Selected TableRowStyle
  54. }
  55. // TableStyles describes all styles of the table header and rows
  56. type TableStyles struct {
  57. Header *TableHeaderStyle
  58. Row *TableRowStyles
  59. }
  60. type tableRow struct {
  61. height float32 // row height
  62. cells []*tableCell // array of row cells
  63. }
  64. type tableCell struct {
  65. Panel // embedded panel
  66. label Label // cell label
  67. value interface{} // cell current value
  68. }
  69. // NewTable creates and returns a pointer to a new Table with the
  70. // specified width, height and columns
  71. func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
  72. t := new(Table)
  73. t.Panel.Initialize(width, height)
  74. t.styles = &StyleDefault.Table
  75. // Checks columns descriptors
  76. t.colmap = make(map[string]*TableColumn)
  77. t.cols = make([]TableColumn, len(cols))
  78. copy(t.cols, cols)
  79. for i := 0; i < len(t.cols); i++ {
  80. c := &t.cols[i]
  81. if c.Format == "" {
  82. c.Format = "%v"
  83. }
  84. c.order = i
  85. if c.Id == "" {
  86. return nil, fmt.Errorf("Column with empty id")
  87. }
  88. if t.colmap[c.Id] != nil {
  89. return nil, fmt.Errorf("Column with duplicate id")
  90. }
  91. t.colmap[c.Id] = c
  92. }
  93. // Create header panels
  94. for i := 0; i < len(t.cols); i++ {
  95. c := &t.cols[i]
  96. c.header = NewPanel(0, 0)
  97. t.applyHeaderStyle(c.header)
  98. c.label = NewLabel(c.Name)
  99. c.header.Add(c.label)
  100. width := c.Width
  101. if width < c.label.Width()+c.header.MinWidth() {
  102. width = c.label.Width() + c.header.MinWidth()
  103. }
  104. c.header.SetContentSize(width, c.label.Height())
  105. t.headerHeight = c.header.Height()
  106. t.Panel.Add(c.header)
  107. }
  108. t.recalcHeader()
  109. return t, nil
  110. }
  111. // SetRows clears all current rows of the table and
  112. // sets new rows from the specifying parameter.
  113. // Each row is a map keyed by the colum id.
  114. // The map value currently can be a string or any number type
  115. // If a row column is not found it is ignored
  116. func (t *Table) SetRows(rows []map[string]interface{}) {
  117. // Create rows if necessary
  118. if len(rows) > len(t.rows) {
  119. count := len(rows) - len(t.rows)
  120. for ri := 0; ri < count; ri++ {
  121. var trow tableRow
  122. trow.cells = make([]*tableCell, 0)
  123. for coli := 0; coli < len(t.cols); coli++ {
  124. cell := new(tableCell)
  125. cell.Initialize(10, 10)
  126. cell.label.initialize("", StyleDefault.Font)
  127. cell.Add(&cell.label)
  128. cell.SetBorders(1, 1, 1, 1)
  129. trow.cells = append(trow.cells, cell)
  130. t.Panel.Add(cell)
  131. }
  132. t.rows = append(t.rows, trow)
  133. }
  134. }
  135. for ri := 0; ri < len(rows); ri++ {
  136. t.SetRow(ri, rows[ri])
  137. }
  138. t.firstRow = 0
  139. t.recalc()
  140. }
  141. func (t *Table) SetRow(row int, values map[string]interface{}) {
  142. if row < 0 || row >= len(t.rows) {
  143. panic("Invalid row index")
  144. }
  145. for ci := 0; ci < len(t.cols); ci++ {
  146. c := t.cols[ci]
  147. cv := values[c.Id]
  148. if cv == nil {
  149. continue
  150. }
  151. t.SetCell(row, c.Id, values[c.Id])
  152. }
  153. }
  154. func (t *Table) SetCell(row int, colid string, value interface{}) {
  155. if row < 0 || row >= len(t.rows) {
  156. panic("Invalid row index")
  157. }
  158. c := t.colmap[colid]
  159. if c == nil {
  160. return
  161. }
  162. cell := t.rows[row].cells[c.order]
  163. cell.label.SetText(fmt.Sprintf(c.Format, value))
  164. }
  165. // SetColFormat sets the formatting string (Printf) for the specified column
  166. // Update must be called to update the table.
  167. func (t *Table) SetColFormat(id, format string) error {
  168. c := t.colmap[id]
  169. if c == nil {
  170. return fmt.Errorf("No column with id:%s", id)
  171. }
  172. c.Format = format
  173. return nil
  174. }
  175. // recalcHeader recalculates and sets the position and size of the header panels
  176. func (t *Table) recalcHeader() {
  177. posx := float32(0)
  178. for i := 0; i < len(t.cols); i++ {
  179. c := t.cols[i]
  180. if c.Hidden {
  181. continue
  182. }
  183. c.header.SetPosition(posx, 0)
  184. posx += c.header.Width()
  185. }
  186. }
  187. // recalc calculates the positions...
  188. func (t *Table) recalc() {
  189. // Assumes that the TableColum array is sorted in show order
  190. py := t.headerHeight
  191. for ri := t.firstRow; ri < len(t.rows); ri++ {
  192. row := t.rows[ri]
  193. px := float32(0)
  194. // Get maximum height for row
  195. for ci := 0; ci < len(t.cols); ci++ {
  196. // If column is hidden, ignore
  197. c := t.cols[ci]
  198. if c.Hidden {
  199. continue
  200. }
  201. cell := row.cells[c.order]
  202. cellHeight := cell.MinHeight() + cell.label.Height()
  203. if cellHeight > row.height {
  204. row.height = cellHeight
  205. }
  206. }
  207. // Sets position and size of row cells
  208. for ci := 0; ci < len(t.cols); ci++ {
  209. // If column is hidden, ignore
  210. c := t.cols[ci]
  211. if c.Hidden {
  212. continue
  213. }
  214. // Sets cell position and size
  215. cell := row.cells[c.order]
  216. cell.SetPosition(px, py)
  217. cell.SetSize(c.header.Width(), row.height)
  218. log.Error("Cell(%v,%v)(%p) size:%v/%v pos:%v/%v", ri, c.Id, &cell, cell.Width(), cell.Height(), cell.Position().X, cell.Position().Y)
  219. px += cell.Width()
  220. }
  221. py += row.height
  222. }
  223. }
  224. // colAtOrder returns the TableColumn at the specified view order.
  225. // Returns nil if not found
  226. func (t *Table) colAtOrder(order int) *TableColumn {
  227. for ci := 0; ci < len(t.cols); ci++ {
  228. if t.cols[ci].order == order {
  229. return &t.cols[ci]
  230. }
  231. }
  232. return nil
  233. }
  234. // applyStyle applies the specified menu body style
  235. func (t *Table) applyHeaderStyle(hp *Panel) {
  236. s := t.styles.Header
  237. hp.SetBordersFrom(&s.Border)
  238. hp.SetBordersColor4(&s.BorderColor)
  239. hp.SetPaddingsFrom(&s.Paddings)
  240. hp.SetColor(&s.BgColor)
  241. }