table.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  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. }
  177. // SetCell sets the value of the cell specified by its row and column id
  178. func (t *Table) SetCell(row int, colid string, value interface{}) {
  179. if row < 0 || row >= len(t.rows) {
  180. panic("Invalid row index")
  181. }
  182. c := t.colmap[colid]
  183. if c == nil {
  184. return
  185. }
  186. cell := t.rows[row].cells[c.order]
  187. cell.label.SetText(fmt.Sprintf(c.Format, value))
  188. }
  189. // SetColFormat sets the formatting string (Printf) for the specified column
  190. // Update must be called to update the table.
  191. func (t *Table) SetColFormat(id, format string) error {
  192. c := t.colmap[id]
  193. if c == nil {
  194. return fmt.Errorf("No column with id:%s", id)
  195. }
  196. c.Format = format
  197. return nil
  198. }
  199. // InsertRow inserts the specified values in a new row at the specified index
  200. func (t *Table) InsertRow(row int, values map[string]interface{}) {
  201. t.insertRow(row, values)
  202. t.recalc()
  203. }
  204. // RemoveRow removes from the specified row from the table
  205. func (t *Table) RemoveRow(row int) {
  206. t.removeRow(row)
  207. t.recalc()
  208. }
  209. // insertRow is the internal version of InsertRow which does not call recalc()
  210. func (t *Table) insertRow(row int, values map[string]interface{}) {
  211. // Checks row index
  212. if row < 0 || row > len(t.rows) {
  213. panic("Invalid row index")
  214. }
  215. // Creates tableRow
  216. trow := new(tableRow)
  217. trow.cells = make([]*tableCell, 0)
  218. for ci := 0; ci < len(t.cols); ci++ {
  219. cell := new(tableCell)
  220. cell.Initialize(0, 0)
  221. cell.label.initialize("", StyleDefault.Font)
  222. cell.Add(&cell.label)
  223. trow.cells = append(trow.cells, cell)
  224. t.Panel.Add(cell)
  225. }
  226. // Inserts tableRow in the table rows at the specified index
  227. t.rows = append(t.rows, nil)
  228. copy(t.rows[row+1:], t.rows[row:])
  229. t.rows[row] = trow
  230. // Sets the new row values from the specified map
  231. if values != nil {
  232. t.SetRow(row, values)
  233. }
  234. }
  235. // removeRow removes from the table the row specified its index
  236. func (t *Table) removeRow(row int) {
  237. // Checks row index
  238. if row < 0 || row >= len(t.rows) {
  239. panic("Invalid row index")
  240. }
  241. // Get row to be removed
  242. trow := t.rows[row]
  243. // Remove row from table
  244. copy(t.rows[row:], t.rows[row+1:])
  245. t.rows[len(t.rows)-1] = nil
  246. t.rows = t.rows[:len(t.rows)-1]
  247. // Dispose the row cell panels and its children
  248. for i := 0; i < len(trow.cells); i++ {
  249. cell := trow.cells[i]
  250. cell.DisposeChildren(true)
  251. cell.Dispose()
  252. }
  253. // Adjusts table first visible row if necessary
  254. //if t.firstRow == row {
  255. // t.firstRow--
  256. // if t.firstRow < 0 {
  257. // t.firstRow = 0
  258. // }
  259. //}
  260. }
  261. // AddRow adds a new row at the end of the table with the specified values
  262. func (t *Table) AddRow(values map[string]interface{}) {
  263. t.InsertRow(len(t.rows), values)
  264. }
  265. // recalcHeader recalculates and sets the position and size of the header panels
  266. func (t *Table) recalcHeader() {
  267. posx := float32(0)
  268. for i := 0; i < len(t.cols); i++ {
  269. c := t.cols[i]
  270. if c.Hidden {
  271. continue
  272. }
  273. c.header.SetPosition(posx, 0)
  274. posx += c.header.Width()
  275. }
  276. }
  277. // recalc calculates the visibility, positions and sizes of all row cells.
  278. // should be called in the following situations:
  279. // - the table is resized
  280. // - row is added, inserted or removed
  281. // - column alignment and expansion changed
  282. // - column visibility is changed
  283. // - horizontal or vertical scroll position changed
  284. func (t *Table) recalc() {
  285. // Get initial Y coordinate and total height of the table
  286. starty := t.headerHeight
  287. if !t.showHeader {
  288. starty = 0
  289. }
  290. theight := t.ContentHeight()
  291. // Calculates all visible rows heights and determines if it
  292. // is necessary to show the scrollbar or not.
  293. scroll := false
  294. py := starty
  295. for ri := t.firstRow; ri < len(t.rows); ri++ {
  296. trow := t.rows[ri]
  297. t.updateRowStyle(ri)
  298. // Get maximum height for row
  299. for ci := 0; ci < len(t.cols); ci++ {
  300. // If column is hidden, ignore
  301. c := t.cols[ci]
  302. if c.Hidden {
  303. continue
  304. }
  305. cell := trow.cells[c.order]
  306. cellHeight := cell.MinHeight() + cell.label.Height()
  307. if cellHeight > trow.height {
  308. trow.height = cellHeight
  309. }
  310. }
  311. py += trow.height
  312. log.Error("row:%v py:%v row height:%v theight:%v", ri, py, trow.height, theight)
  313. if py > theight {
  314. scroll = true
  315. t.lastRow = ri
  316. break
  317. }
  318. }
  319. t.setVScrollBar(scroll)
  320. // Assumes that the TableColum array is sorted in show order
  321. py = starty
  322. for ri := t.firstRow; ri < len(t.rows); ri++ {
  323. trow := t.rows[ri]
  324. t.updateRowStyle(ri)
  325. px := float32(0)
  326. // Sets position and size of row cells
  327. for ci := 0; ci < len(t.cols); ci++ {
  328. // If column is hidden, ignore
  329. c := t.cols[ci]
  330. if c.Hidden {
  331. continue
  332. }
  333. // Sets cell position and size
  334. cell := trow.cells[c.order]
  335. cell.SetPosition(px, py)
  336. cell.SetSize(c.header.Width(), trow.height)
  337. //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)
  338. px += c.header.Width()
  339. }
  340. py += trow.height
  341. if py > theight {
  342. break
  343. }
  344. }
  345. }
  346. func (t *Table) sortCols() {
  347. }
  348. // setVScrollBar sets the visibility state of the vertical scrollbar
  349. func (t *Table) setVScrollBar(state bool) {
  350. // Visible
  351. if state {
  352. var scrollWidth float32 = 20
  353. // Creates scroll bar if necessary
  354. if t.vscroll == nil {
  355. t.vscroll = NewVScrollBar(0, 0)
  356. t.vscroll.SetBorders(0, 0, 0, 1)
  357. t.vscroll.Subscribe(OnChange, t.onVScrollBarEvent)
  358. t.Panel.Add(t.vscroll)
  359. }
  360. // Initial y coordinate and height
  361. py := float32(0)
  362. height := t.ContentHeight()
  363. if t.showHeader {
  364. py = t.headerHeight
  365. height -= py
  366. }
  367. t.vscroll.SetSize(scrollWidth, height)
  368. t.vscroll.SetPositionX(t.ContentWidth() - scrollWidth)
  369. t.vscroll.SetPositionY(py)
  370. t.vscroll.recalc()
  371. t.vscroll.SetVisible(true)
  372. // Not visible
  373. } else {
  374. if t.vscroll != nil {
  375. t.vscroll.SetVisible(false)
  376. }
  377. }
  378. }
  379. func (t *Table) onVScrollBarEvent(evname string, ev interface{}) {
  380. pos := t.vscroll.Value()
  381. maxFirst := len(t.rows) - (t.lastRow - t.firstRow + 1)
  382. first := int(math.Floor((float64(maxFirst) * pos) + 0.5))
  383. if first == t.firstRow {
  384. return
  385. }
  386. log.Error("maxFirst:%v firstRow:%v", maxFirst, first)
  387. //s.scrollBarEvent = true
  388. t.firstRow = first
  389. t.recalc()
  390. }
  391. // updateRowStyle applies the correct style for the specified row
  392. func (t *Table) updateRowStyle(ri int) {
  393. row := t.rows[ri]
  394. if row.selected {
  395. t.applyRowStyle(row, &t.styles.Row.Selected)
  396. return
  397. }
  398. t.applyRowStyle(row, &t.styles.Row.Normal)
  399. }
  400. // applyRowStyle applies the specified style to all cells for the specified table row
  401. func (t *Table) applyRowStyle(row *tableRow, trs *TableRowStyle) {
  402. for i := 0; i < len(row.cells); i++ {
  403. cell := row.cells[i]
  404. cell.SetBordersFrom(&trs.Border)
  405. cell.SetBordersColor4(&trs.BorderColor)
  406. cell.SetPaddingsFrom(&trs.Paddings)
  407. cell.SetColor(&trs.BgColor)
  408. }
  409. }
  410. // applyStyle applies the specified menu body style
  411. func (t *Table) applyHeaderStyle(hp *Panel) {
  412. s := t.styles.Header
  413. hp.SetBordersFrom(&s.Border)
  414. hp.SetBordersColor4(&s.BorderColor)
  415. hp.SetPaddingsFrom(&s.Paddings)
  416. hp.SetColor(&s.BgColor)
  417. }