table.go 13 KB

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