table.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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. Must be unique
  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. Alignment Align // Cell content alignment: AlignNone|AlignLeft|AlignCenter|AlignRight
  30. Expand int // Width expansion factor
  31. order int // show order
  32. header *Panel // header panel
  33. label *Label // header label
  34. }
  35. // TableHeaderStyle describes the style of the table header
  36. type TableHeaderStyle struct {
  37. Border BorderSizes
  38. Paddings BorderSizes
  39. BorderColor math32.Color4
  40. BgColor math32.Color
  41. FgColor math32.Color
  42. }
  43. // TableRowStyle describes the style of the table row
  44. type TableRowStyle struct {
  45. Border BorderSizes
  46. Paddings BorderSizes
  47. BorderColor math32.Color4
  48. BgColor math32.Color
  49. FgColor math32.Color
  50. }
  51. // TableRowStyles describes all styles for the table row
  52. type TableRowStyles struct {
  53. Normal TableRowStyle
  54. Selected TableRowStyle
  55. }
  56. // TableStyles describes all styles of the table header and rows
  57. type TableStyles struct {
  58. Header *TableHeaderStyle
  59. Row *TableRowStyles
  60. }
  61. type tableRow struct {
  62. height float32 // row height
  63. selected bool // row selected flag
  64. cells []*tableCell // array of row cells
  65. }
  66. type tableCell struct {
  67. Panel // embedded panel
  68. label Label // cell label
  69. value interface{} // cell current value
  70. }
  71. // NewTable creates and returns a pointer to a new Table with the
  72. // specified width, height and columns
  73. func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
  74. t := new(Table)
  75. t.Panel.Initialize(width, height)
  76. t.styles = &StyleDefault.Table
  77. // Checks columns descriptors
  78. t.colmap = make(map[string]*TableColumn)
  79. t.cols = make([]TableColumn, len(cols))
  80. copy(t.cols, cols)
  81. for i := 0; i < len(t.cols); i++ {
  82. c := &t.cols[i]
  83. if c.Format == "" {
  84. c.Format = "%v"
  85. }
  86. c.order = i
  87. if c.Id == "" {
  88. return nil, fmt.Errorf("Column with empty id")
  89. }
  90. if t.colmap[c.Id] != nil {
  91. return nil, fmt.Errorf("Column with duplicate id")
  92. }
  93. t.colmap[c.Id] = c
  94. }
  95. // Create header panels
  96. for i := 0; i < len(t.cols); i++ {
  97. c := &t.cols[i]
  98. c.header = NewPanel(0, 0)
  99. t.applyHeaderStyle(c.header)
  100. c.label = NewLabel(c.Name)
  101. c.header.Add(c.label)
  102. width := c.Width
  103. if width < c.label.Width()+c.header.MinWidth() {
  104. width = c.label.Width() + c.header.MinWidth()
  105. }
  106. c.header.SetContentSize(width, c.label.Height())
  107. t.headerHeight = c.header.Height()
  108. t.Panel.Add(c.header)
  109. }
  110. t.recalcHeader()
  111. t.Subscribe(OnResize, func(evname string, ev interface{}) {
  112. t.recalc()
  113. })
  114. return t, nil
  115. }
  116. // SetRows clears all current rows of the table and
  117. // sets new rows from the specifying parameter.
  118. // Each row is a map keyed by the colum id.
  119. // The map value currently can be a string or any number type
  120. // If a row column is not found it is ignored
  121. func (t *Table) SetRows(rows []map[string]interface{}) {
  122. // Create rows if necessary
  123. if len(rows) > len(t.rows) {
  124. count := len(rows) - len(t.rows)
  125. for ri := 0; ri < count; ri++ {
  126. trow := new(tableRow)
  127. trow.cells = make([]*tableCell, 0)
  128. for ci := 0; ci < len(t.cols); ci++ {
  129. cell := new(tableCell)
  130. cell.Initialize(0, 0)
  131. cell.label.initialize("", StyleDefault.Font)
  132. cell.Add(&cell.label)
  133. trow.cells = append(trow.cells, cell)
  134. t.Panel.Add(cell)
  135. }
  136. t.rows = append(t.rows, trow)
  137. }
  138. }
  139. for ri := 0; ri < len(rows); ri++ {
  140. t.SetRow(ri, rows[ri])
  141. }
  142. t.firstRow = 0
  143. t.recalc()
  144. }
  145. // SetRow sets the value of all the cells of the specified row from
  146. // the specified map indexed by column id.
  147. func (t *Table) SetRow(row int, values map[string]interface{}) {
  148. if row < 0 || row >= len(t.rows) {
  149. panic("Invalid row index")
  150. }
  151. for ci := 0; ci < len(t.cols); ci++ {
  152. c := t.cols[ci]
  153. cv := values[c.Id]
  154. if cv == nil {
  155. continue
  156. }
  157. t.SetCell(row, c.Id, values[c.Id])
  158. }
  159. }
  160. // SetCell sets the value of the cell specified by its row and column id
  161. func (t *Table) SetCell(row int, colid string, value interface{}) {
  162. if row < 0 || row >= len(t.rows) {
  163. panic("Invalid row index")
  164. }
  165. c := t.colmap[colid]
  166. if c == nil {
  167. return
  168. }
  169. cell := t.rows[row].cells[c.order]
  170. cell.label.SetText(fmt.Sprintf(c.Format, value))
  171. }
  172. // SetColFormat sets the formatting string (Printf) for the specified column
  173. // Update must be called to update the table.
  174. func (t *Table) SetColFormat(id, format string) error {
  175. c := t.colmap[id]
  176. if c == nil {
  177. return fmt.Errorf("No column with id:%s", id)
  178. }
  179. c.Format = format
  180. return nil
  181. }
  182. func (t *Table) AddRow(values map[string]interface{}) {
  183. }
  184. // recalcHeader recalculates and sets the position and size of the header panels
  185. func (t *Table) recalcHeader() {
  186. posx := float32(0)
  187. for i := 0; i < len(t.cols); i++ {
  188. c := t.cols[i]
  189. if c.Hidden {
  190. continue
  191. }
  192. c.header.SetPosition(posx, 0)
  193. posx += c.header.Width()
  194. }
  195. }
  196. // recalc calculates the visibility, positions and sizes of all row cells.
  197. // should be called in the following situations:
  198. // - the table is resized
  199. // - row is added, inserted or removed
  200. // - column alignment and expansion changed
  201. // - column visibility is changed
  202. // - horizontal or vertical scroll position changed
  203. func (t *Table) recalc() {
  204. // Assumes that the TableColum array is sorted in show order
  205. py := t.headerHeight
  206. for ri := t.firstRow; ri < len(t.rows); ri++ {
  207. row := t.rows[ri]
  208. t.updateRowStyle(ri)
  209. px := float32(0)
  210. // Get maximum height for row
  211. for ci := 0; ci < len(t.cols); ci++ {
  212. // If column is hidden, ignore
  213. c := t.cols[ci]
  214. if c.Hidden {
  215. continue
  216. }
  217. cell := row.cells[c.order]
  218. cellHeight := cell.MinHeight() + cell.label.Height()
  219. if cellHeight > row.height {
  220. row.height = cellHeight
  221. }
  222. }
  223. // Sets position and size of row cells
  224. for ci := 0; ci < len(t.cols); ci++ {
  225. // If column is hidden, ignore
  226. c := t.cols[ci]
  227. if c.Hidden {
  228. continue
  229. }
  230. // Sets cell position and size
  231. cell := row.cells[c.order]
  232. cell.SetPosition(px, py)
  233. cell.SetSize(c.header.Width(), row.height)
  234. //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)
  235. px += c.header.Width()
  236. }
  237. py += row.height
  238. if py > t.Height() {
  239. break
  240. }
  241. }
  242. }
  243. func (t *Table) sortCols() {
  244. }
  245. // updateRowStyle applies the correct style for the specified row
  246. func (t *Table) updateRowStyle(ri int) {
  247. row := t.rows[ri]
  248. if row.selected {
  249. t.applyRowStyle(row, &t.styles.Row.Selected)
  250. return
  251. }
  252. t.applyRowStyle(row, &t.styles.Row.Normal)
  253. }
  254. // applyRowStyle applies the specified style to all cells for the specified table row
  255. func (t *Table) applyRowStyle(row *tableRow, trs *TableRowStyle) {
  256. for i := 0; i < len(row.cells); i++ {
  257. cell := row.cells[i]
  258. cell.SetBordersFrom(&trs.Border)
  259. cell.SetBordersColor4(&trs.BorderColor)
  260. cell.SetPaddingsFrom(&trs.Paddings)
  261. cell.SetColor(&trs.BgColor)
  262. }
  263. }
  264. // applyStyle applies the specified menu body style
  265. func (t *Table) applyHeaderStyle(hp *Panel) {
  266. s := t.styles.Header
  267. hp.SetBordersFrom(&s.Border)
  268. hp.SetBordersColor4(&s.BorderColor)
  269. hp.SetPaddingsFrom(&s.Paddings)
  270. hp.SetColor(&s.BgColor)
  271. }