table.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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 visible row
  19. lastRow int // index of the last visible row
  20. rows []*tableRow // array of table rows
  21. headerHeight float32 // header height
  22. vscroll *ScrollBar // vertical scroll bar
  23. showHeader bool
  24. }
  25. // TableColumn describes a table column
  26. type TableColumn struct {
  27. Id string // Column id used to reference the column. Must be unique
  28. Name string // Column name shown in the header
  29. Width float32 // Column preferable width in pixels
  30. Hidden bool // Hidden flag
  31. Format string // Format string for numbers and strings
  32. Alignment Align // Cell content alignment: AlignNone|AlignLeft|AlignCenter|AlignRight
  33. Expand int // Width expansion factor
  34. order int // show order
  35. header *Panel // header panel
  36. label *Label // header label
  37. }
  38. // TableHeaderStyle describes the style of the table header
  39. type TableHeaderStyle struct {
  40. Border BorderSizes
  41. Paddings BorderSizes
  42. BorderColor math32.Color4
  43. BgColor math32.Color
  44. FgColor math32.Color
  45. }
  46. // TableRowStyle describes the style of the table row
  47. type TableRowStyle struct {
  48. Border BorderSizes
  49. Paddings BorderSizes
  50. BorderColor math32.Color4
  51. BgColor math32.Color
  52. FgColor math32.Color
  53. }
  54. // TableRowStyles describes all styles for the table row
  55. type TableRowStyles struct {
  56. Normal TableRowStyle
  57. Selected TableRowStyle
  58. }
  59. // TableStyles describes all styles of the table header and rows
  60. type TableStyles struct {
  61. Header *TableHeaderStyle
  62. Row *TableRowStyles
  63. }
  64. type tableRow struct {
  65. height float32 // row height
  66. selected bool // row selected flag
  67. cells []*tableCell // array of row cells
  68. }
  69. type tableCell struct {
  70. Panel // embedded panel
  71. label Label // cell label
  72. value interface{} // cell current value
  73. }
  74. // NewTable creates and returns a pointer to a new Table with the
  75. // specified width, height and columns
  76. func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
  77. t := new(Table)
  78. t.Panel.Initialize(width, height)
  79. t.styles = &StyleDefault.Table
  80. t.showHeader = true
  81. // Checks columns descriptors
  82. t.colmap = make(map[string]*TableColumn)
  83. t.cols = make([]TableColumn, len(cols))
  84. copy(t.cols, cols)
  85. for i := 0; i < len(t.cols); i++ {
  86. c := &t.cols[i]
  87. if c.Format == "" {
  88. c.Format = "%v"
  89. }
  90. c.order = i
  91. if c.Id == "" {
  92. return nil, fmt.Errorf("Column with empty id")
  93. }
  94. if t.colmap[c.Id] != nil {
  95. return nil, fmt.Errorf("Column with duplicate id")
  96. }
  97. t.colmap[c.Id] = c
  98. }
  99. // Create header panels
  100. for i := 0; i < len(t.cols); i++ {
  101. c := &t.cols[i]
  102. c.header = NewPanel(0, 0)
  103. t.applyHeaderStyle(c.header)
  104. c.label = NewLabel(c.Name)
  105. c.header.Add(c.label)
  106. width := c.Width
  107. if width < c.label.Width()+c.header.MinWidth() {
  108. width = c.label.Width() + c.header.MinWidth()
  109. }
  110. c.header.SetContentSize(width, c.label.Height())
  111. t.headerHeight = c.header.Height()
  112. t.Panel.Add(c.header)
  113. }
  114. t.recalcHeader()
  115. t.Subscribe(OnResize, func(evname string, ev interface{}) {
  116. t.recalc()
  117. })
  118. return t, nil
  119. }
  120. // ShowHeaders shows or hides the table header
  121. func (t *Table) ShowHeader(show bool) {
  122. if t.showHeader == show {
  123. return
  124. }
  125. t.showHeader = show
  126. for i := 0; i < len(t.cols); i++ {
  127. c := &t.cols[i]
  128. c.header.SetVisible(t.showHeader)
  129. }
  130. t.recalc()
  131. }
  132. // Len returns the total number of rows of the table
  133. func (t *Table) Len() int {
  134. return len(t.rows)
  135. }
  136. // SetRows clears all current rows of the table and
  137. // sets new rows from the specifying parameter.
  138. // Each row is a map keyed by the colum id.
  139. // The map value currently can be a string or any number type
  140. // If a row column is not found it is ignored
  141. func (t *Table) SetRows(values []map[string]interface{}) {
  142. // Add missing rows
  143. if len(values) > len(t.rows) {
  144. count := len(values) - len(t.rows)
  145. for row := 0; row < count; row++ {
  146. t.insertRow(len(t.rows), nil)
  147. }
  148. // Remove remaining rows
  149. } else if len(values) < len(t.rows) {
  150. for row := len(values); row < len(t.rows); row++ {
  151. t.removeRow(row)
  152. }
  153. }
  154. // Set rows values
  155. for row := 0; row < len(values); row++ {
  156. t.SetRow(row, values[row])
  157. }
  158. t.firstRow = 0
  159. t.recalc()
  160. }
  161. // SetRow sets the value of all the cells of the specified row from
  162. // the specified map indexed by column id.
  163. func (t *Table) SetRow(row int, values map[string]interface{}) {
  164. if row < 0 || row >= len(t.rows) {
  165. panic("Invalid row index")
  166. }
  167. for ci := 0; ci < len(t.cols); ci++ {
  168. c := t.cols[ci]
  169. cv := values[c.Id]
  170. if cv == nil {
  171. continue
  172. }
  173. t.SetCell(row, c.Id, values[c.Id])
  174. }
  175. }
  176. // SetCell sets the value of the cell specified by its row and column id
  177. func (t *Table) SetCell(row int, colid string, value interface{}) {
  178. if row < 0 || row >= len(t.rows) {
  179. panic("Invalid row index")
  180. }
  181. c := t.colmap[colid]
  182. if c == nil {
  183. return
  184. }
  185. cell := t.rows[row].cells[c.order]
  186. cell.label.SetText(fmt.Sprintf(c.Format, value))
  187. }
  188. // SetColFormat sets the formatting string (Printf) for the specified column
  189. // Update must be called to update the table.
  190. func (t *Table) SetColFormat(id, format string) error {
  191. c := t.colmap[id]
  192. if c == nil {
  193. return fmt.Errorf("No column with id:%s", id)
  194. }
  195. c.Format = format
  196. return nil
  197. }
  198. // InsertRow inserts the specified values in a new row at the specified index
  199. func (t *Table) InsertRow(row int, values map[string]interface{}) {
  200. t.insertRow(row, values)
  201. t.recalc()
  202. }
  203. // RemoveRow removes from the specified row from the table
  204. func (t *Table) RemoveRow(row int) {
  205. t.removeRow(row)
  206. t.recalc()
  207. }
  208. // insertRow is the internal version of InsertRow which does not call recalc()
  209. func (t *Table) insertRow(row int, values map[string]interface{}) {
  210. // Checks row index
  211. if row < 0 || row > len(t.rows) {
  212. panic("Invalid row index")
  213. }
  214. // Creates tableRow
  215. trow := new(tableRow)
  216. trow.cells = make([]*tableCell, 0)
  217. for ci := 0; ci < len(t.cols); ci++ {
  218. cell := new(tableCell)
  219. cell.Initialize(0, 0)
  220. cell.label.initialize("", StyleDefault.Font)
  221. cell.Add(&cell.label)
  222. trow.cells = append(trow.cells, cell)
  223. t.Panel.Add(cell)
  224. }
  225. // Inserts tableRow in the table rows at the specified index
  226. t.rows = append(t.rows, nil)
  227. copy(t.rows[row+1:], t.rows[row:])
  228. t.rows[row] = trow
  229. // Sets the new row values from the specified map
  230. if values != nil {
  231. t.SetRow(row, values)
  232. }
  233. }
  234. // removeRow removes from the table the row specified its index
  235. func (t *Table) removeRow(row int) {
  236. // Checks row index
  237. if row < 0 || row >= len(t.rows) {
  238. panic("Invalid row index")
  239. }
  240. // Get row to be removed
  241. trow := t.rows[row]
  242. // Remove row from table
  243. copy(t.rows[row:], t.rows[row+1:])
  244. t.rows[len(t.rows)-1] = nil
  245. t.rows = t.rows[:len(t.rows)-1]
  246. // Dispose the row cell panels and its children
  247. for i := 0; i < len(trow.cells); i++ {
  248. cell := trow.cells[i]
  249. cell.DisposeChildren(true)
  250. cell.Dispose()
  251. }
  252. // Adjusts table first visible row if necessary
  253. //if t.firstRow == row {
  254. // t.firstRow--
  255. // if t.firstRow < 0 {
  256. // t.firstRow = 0
  257. // }
  258. //}
  259. }
  260. // AddRow adds a new row at the end of the table with the specified values
  261. func (t *Table) AddRow(values map[string]interface{}) {
  262. t.InsertRow(len(t.rows), values)
  263. }
  264. // recalcHeader recalculates and sets the position and size of the header panels
  265. func (t *Table) recalcHeader() {
  266. posx := float32(0)
  267. for i := 0; i < len(t.cols); i++ {
  268. c := t.cols[i]
  269. if c.Hidden {
  270. continue
  271. }
  272. c.header.SetPosition(posx, 0)
  273. posx += c.header.Width()
  274. }
  275. }
  276. // recalc calculates the visibility, positions and sizes of all row cells.
  277. // should be called in the following situations:
  278. // - the table is resized
  279. // - row is added, inserted or removed
  280. // - column alignment and expansion changed
  281. // - column visibility is changed
  282. // - horizontal or vertical scroll position changed
  283. func (t *Table) recalc() {
  284. // Get initial Y coordinate and total height of the table
  285. starty := t.headerHeight
  286. if !t.showHeader {
  287. starty = 0
  288. }
  289. theight := t.ContentHeight()
  290. // Calculates all visible rows heights and determines if it
  291. // is necessary to show the scrollbar or not.
  292. scroll := false
  293. py := starty
  294. for ri := t.firstRow; ri < len(t.rows); ri++ {
  295. trow := t.rows[ri]
  296. t.updateRowStyle(ri)
  297. // Get maximum height for row
  298. for ci := 0; ci < len(t.cols); ci++ {
  299. // If column is hidden, ignore
  300. c := t.cols[ci]
  301. if c.Hidden {
  302. continue
  303. }
  304. cell := trow.cells[c.order]
  305. cellHeight := cell.MinHeight() + cell.label.Height()
  306. if cellHeight > trow.height {
  307. trow.height = cellHeight
  308. }
  309. }
  310. py += trow.height
  311. log.Error("row:%v py:%v row height:%v theight:%v", ri, py, trow.height, theight)
  312. if py > theight {
  313. scroll = true
  314. t.lastRow = ri
  315. break
  316. }
  317. }
  318. t.setVScrollBar(scroll)
  319. // Assumes that the TableColum array is sorted in show order
  320. py = starty
  321. for ri := t.firstRow; ri < len(t.rows); ri++ {
  322. trow := t.rows[ri]
  323. t.updateRowStyle(ri)
  324. px := float32(0)
  325. // Sets position and size of row cells
  326. for ci := 0; ci < len(t.cols); ci++ {
  327. // If column is hidden, ignore
  328. c := t.cols[ci]
  329. if c.Hidden {
  330. continue
  331. }
  332. // Sets cell position and size
  333. cell := trow.cells[c.order]
  334. cell.SetPosition(px, py)
  335. cell.SetSize(c.header.Width(), trow.height)
  336. //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)
  337. px += c.header.Width()
  338. }
  339. py += trow.height
  340. if py > theight {
  341. break
  342. }
  343. }
  344. }
  345. func (t *Table) sortCols() {
  346. }
  347. // setVScrollBar sets the visibility state of the vertical scrollbar
  348. func (t *Table) setVScrollBar(state bool) {
  349. // Visible
  350. if state {
  351. var scrollWidth float32 = 20
  352. // Creates scroll bar if necessary
  353. if t.vscroll == nil {
  354. t.vscroll = NewVScrollBar(0, 0)
  355. t.vscroll.SetBorders(0, 0, 0, 1)
  356. //t.vscroll.Subscribe(OnChange, s.onScrollBarEvent)
  357. t.Panel.Add(t.vscroll)
  358. }
  359. // Initial y coordinate and height
  360. py := float32(0)
  361. height := t.ContentHeight()
  362. if t.showHeader {
  363. py = t.headerHeight
  364. height -= py
  365. }
  366. t.vscroll.SetSize(scrollWidth, height)
  367. t.vscroll.SetPositionX(t.ContentWidth() - scrollWidth)
  368. t.vscroll.SetPositionY(py)
  369. t.vscroll.recalc()
  370. t.vscroll.SetVisible(true)
  371. // Not visible
  372. } else {
  373. if t.vscroll != nil {
  374. t.vscroll.SetVisible(false)
  375. }
  376. }
  377. }
  378. // updateRowStyle applies the correct style for the specified row
  379. func (t *Table) updateRowStyle(ri int) {
  380. row := t.rows[ri]
  381. if row.selected {
  382. t.applyRowStyle(row, &t.styles.Row.Selected)
  383. return
  384. }
  385. t.applyRowStyle(row, &t.styles.Row.Normal)
  386. }
  387. // applyRowStyle applies the specified style to all cells for the specified table row
  388. func (t *Table) applyRowStyle(row *tableRow, trs *TableRowStyle) {
  389. for i := 0; i < len(row.cells); i++ {
  390. cell := row.cells[i]
  391. cell.SetBordersFrom(&trs.Border)
  392. cell.SetBordersColor4(&trs.BorderColor)
  393. cell.SetPaddingsFrom(&trs.Paddings)
  394. cell.SetColor(&trs.BgColor)
  395. }
  396. }
  397. // applyStyle applies the specified menu body style
  398. func (t *Table) applyHeaderStyle(hp *Panel) {
  399. s := t.styles.Header
  400. hp.SetBordersFrom(&s.Border)
  401. hp.SetBordersColor4(&s.BorderColor)
  402. hp.SetPaddingsFrom(&s.Paddings)
  403. hp.SetColor(&s.BgColor)
  404. }