table.go 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  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. "math"
  8. "github.com/g3n/engine/math32"
  9. "github.com/g3n/engine/window"
  10. )
  11. const (
  12. // Name of the event generated when the table is right or left clicked
  13. // Parameter is TableClickEvent
  14. OnTableClick = "onTableClick"
  15. // Name of the event generated when the table row count changes (no parameters)
  16. OnTableRowCount = "onTableRowCount"
  17. )
  18. //
  19. // Table implements a panel which can contains child panels
  20. // organized in rows and columns.
  21. //
  22. type Table struct {
  23. Panel // Embedded panel
  24. styles *TableStyles // pointer to current styles
  25. header tableHeader // table headers
  26. firstRow int // index of the first visible row
  27. lastRow int // index of the last visible row
  28. rows []*tableRow // array of table rows
  29. vscroll *ScrollBar // vertical scroll bar
  30. statusPanel Panel // optional bottom status panel
  31. statusLabel *Label // status label
  32. }
  33. // TableColumn describes a table column
  34. type TableColumn struct {
  35. Id string // Column id used to reference the column. Must be unique
  36. Name string // Column name shown in the header
  37. Width float32 // Inital column width in pixels
  38. Hidden bool // Hidden flag
  39. Align Align // Cell content alignment: AlignLeft|AlignCenter|AlignRight
  40. Format string // Format string for formatting the columns' cells
  41. FormatFunc TableFormatFunc // Format function (overrides Format string)
  42. Expand int // Column width expansion factor (0 no expansion)
  43. order int // show order
  44. }
  45. // TableCell describes a table cell.
  46. // It is used as a parameter for formatting function
  47. type TableCell struct {
  48. Tab *Table // Pointer to table
  49. Row int // Row index
  50. Col string // Column id
  51. Value interface{} // Cell value
  52. }
  53. // TableFormatFunc is the type for formatting functions
  54. type TableFormatFunc func(cell TableCell) string
  55. // TableHeaderStyle describes the style of the table header
  56. type TableHeaderStyle struct {
  57. Border BorderSizes
  58. Paddings BorderSizes
  59. BorderColor math32.Color4
  60. BgColor math32.Color
  61. FgColor math32.Color
  62. }
  63. // TableRowStyle describes the style of the table row
  64. type TableRowStyle struct {
  65. Border BorderSizes
  66. Paddings BorderSizes
  67. BorderColor math32.Color4
  68. BgColor math32.Color
  69. FgColor math32.Color
  70. }
  71. // TableRowStyles describes all styles for the table row
  72. type TableRowStyles struct {
  73. Normal TableRowStyle
  74. Selected TableRowStyle
  75. }
  76. // TableStatusStyle describes the style of the table status lineow
  77. type TableStatusStyle struct {
  78. Border BorderSizes
  79. Paddings BorderSizes
  80. BorderColor math32.Color4
  81. BgColor math32.Color
  82. FgColor math32.Color
  83. }
  84. // TableStyles describes all styles of the table header and rows
  85. type TableStyles struct {
  86. Header *TableHeaderStyle
  87. Row *TableRowStyles
  88. Status *TableStatusStyle
  89. }
  90. // TableClickEvent describes a mouse click event over a table
  91. // It contains the original mouse event plus additional information
  92. type TableClickEvent struct {
  93. window.MouseEvent // Embedded window mouse event
  94. X float32 // Table content area X coordinate
  95. Y float32 // Table content area Y coordinate
  96. Header bool // True if header was clicked
  97. Row int // Index of table row (may be -1)
  98. Col string // Id of table column (may be empty)
  99. }
  100. // tableHeader is panel which contains the individual header panels for each column
  101. type tableHeader struct {
  102. Panel // embedded panel
  103. cmap map[string]*tableColHeader // maps column id with its panel/descriptor
  104. cols []*tableColHeader // array of individual column headers/descriptors
  105. }
  106. // tableColHeader is panel for a column header
  107. type tableColHeader struct {
  108. Panel // header panel
  109. label *Label // header label
  110. id string // column id
  111. width float32 // initial column width
  112. format string // column format string
  113. formatFunc TableFormatFunc // column format function
  114. align Align // column alignment
  115. expand int // column expand factor
  116. order int // row columns order
  117. }
  118. // tableRow is panel which contains an entire table row of cells
  119. type tableRow struct {
  120. Panel // embedded panel
  121. selected bool // row selected flag
  122. cells []*tableCell // array of row cells
  123. }
  124. // tableCell is a panel which contains one cell (a label)
  125. type tableCell struct {
  126. Panel // embedded panel
  127. label Label // cell label
  128. value interface{} // cell current value
  129. }
  130. // NewTable creates and returns a pointer to a new Table with the
  131. // specified width, height and columns
  132. func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
  133. t := new(Table)
  134. t.Panel.Initialize(width, height)
  135. t.styles = &StyleDefault.Table
  136. // Initialize table header
  137. t.header.Initialize(0, 0)
  138. t.header.cmap = make(map[string]*tableColHeader)
  139. t.header.cols = make([]*tableColHeader, 0)
  140. // Create column header panels
  141. for ci := 0; ci < len(cols); ci++ {
  142. cdesc := cols[ci]
  143. // Column id must not be empty
  144. if cdesc.Id == "" {
  145. return nil, fmt.Errorf("Column with empty id")
  146. }
  147. // Column id must be unique
  148. if t.header.cmap[cdesc.Id] != nil {
  149. return nil, fmt.Errorf("Column with duplicate id")
  150. }
  151. // Creates a column header
  152. c := new(tableColHeader)
  153. c.Initialize(0, 0)
  154. t.applyHeaderStyle(c)
  155. c.label = NewLabel(cdesc.Name)
  156. c.Add(c.label)
  157. c.id = cdesc.Id
  158. c.width = cdesc.Width
  159. c.align = cdesc.Align
  160. c.format = cdesc.Format
  161. c.formatFunc = cdesc.FormatFunc
  162. c.expand = cdesc.Expand
  163. // Sets default format and order
  164. if c.format == "" {
  165. c.format = "%v"
  166. }
  167. c.order = ci
  168. c.SetVisible(!cdesc.Hidden)
  169. t.header.cmap[c.id] = c
  170. // Sets column header width and height
  171. width := cdesc.Width
  172. if width < c.label.Width()+c.MinWidth() {
  173. width = c.label.Width() + c.MinWidth()
  174. }
  175. c.SetContentSize(width, c.label.Height())
  176. // Adds the column header to the header panel
  177. t.header.cols = append(t.header.cols, c)
  178. t.header.Panel.Add(c)
  179. }
  180. t.Panel.Add(&t.header)
  181. t.recalcHeader()
  182. // Creates status panel
  183. t.statusPanel.Initialize(0, 0)
  184. t.statusPanel.SetVisible(false)
  185. t.statusLabel = NewLabel("")
  186. t.applyStatusStyle()
  187. t.statusPanel.Add(t.statusLabel)
  188. t.Panel.Add(&t.statusPanel)
  189. t.recalcStatus()
  190. // Subscribe to events
  191. t.Panel.Subscribe(OnMouseUp, t.onMouse)
  192. t.Panel.Subscribe(OnMouseDown, t.onMouse)
  193. t.Panel.Subscribe(OnKeyDown, t.onKeyEvent)
  194. t.Panel.Subscribe(OnKeyRepeat, t.onKeyEvent)
  195. t.Panel.Subscribe(OnResize, t.onResize)
  196. return t, nil
  197. }
  198. // ShowHeader shows or hides the table header
  199. func (t *Table) ShowHeader(show bool) {
  200. if t.header.Visible() == show {
  201. return
  202. }
  203. t.header.SetVisible(show)
  204. t.recalc()
  205. }
  206. // ShowColumn sets the visibility of the column with the specified id
  207. // If the column id does not exit the function panics.
  208. func (t *Table) ShowColumn(col string, show bool) {
  209. c := t.header.cmap[col]
  210. if c == nil {
  211. panic("Invalid column id")
  212. }
  213. if c.Visible() == show {
  214. return
  215. }
  216. c.SetVisible(show)
  217. t.recalcHeader()
  218. // Recalculates all rows
  219. for ri := 0; ri < len(t.rows); ri++ {
  220. t.recalcRow(ri)
  221. }
  222. t.recalc()
  223. }
  224. // ShowAllColumns shows all the table columns
  225. func (t *Table) ShowAllColumns() {
  226. recalc := false
  227. for ci := 0; ci < len(t.header.cols); ci++ {
  228. c := t.header.cols[ci]
  229. if !c.Visible() {
  230. c.SetVisible(true)
  231. recalc = true
  232. }
  233. }
  234. if !recalc {
  235. return
  236. }
  237. t.recalcHeader()
  238. // Recalculates all rows
  239. for ri := 0; ri < len(t.rows); ri++ {
  240. t.recalcRow(ri)
  241. }
  242. t.recalc()
  243. }
  244. // RowCount returns the current number of rows in the table
  245. func (t *Table) RowCount() int {
  246. return len(t.rows)
  247. }
  248. // SetRows clears all current rows of the table and
  249. // sets new rows from the specifying parameter.
  250. // Each row is a map keyed by the colum id.
  251. // The map value currently can be a string or any number type
  252. // If a row column is not found it is ignored
  253. func (t *Table) SetRows(values []map[string]interface{}) {
  254. // Add missing rows
  255. if len(values) > len(t.rows) {
  256. count := len(values) - len(t.rows)
  257. for row := 0; row < count; row++ {
  258. t.insertRow(len(t.rows), nil)
  259. }
  260. // Remove remaining rows
  261. } else if len(values) < len(t.rows) {
  262. for row := len(values); row < len(t.rows); row++ {
  263. t.removeRow(row)
  264. }
  265. }
  266. // Set rows values
  267. for row := 0; row < len(values); row++ {
  268. t.SetRow(row, values[row])
  269. }
  270. t.firstRow = 0
  271. t.recalc()
  272. }
  273. // SetRow sets the value of all the cells of the specified row from
  274. // the specified map indexed by column id.
  275. func (t *Table) SetRow(row int, values map[string]interface{}) {
  276. if row < 0 || row >= len(t.rows) {
  277. panic("Invalid row index")
  278. }
  279. for ci := 0; ci < len(t.header.cols); ci++ {
  280. c := t.header.cols[ci]
  281. cv, ok := values[c.id]
  282. if !ok {
  283. continue
  284. }
  285. t.SetCell(row, c.id, cv)
  286. }
  287. t.recalcRow(row)
  288. }
  289. // SetCell sets the value of the cell specified by its row and column id
  290. func (t *Table) SetCell(row int, colid string, value interface{}) {
  291. if row < 0 || row >= len(t.rows) {
  292. panic("Invalid row index")
  293. }
  294. c := t.header.cmap[colid]
  295. if c == nil {
  296. return
  297. }
  298. cell := t.rows[row].cells[c.order]
  299. cell.label.SetText(fmt.Sprintf(c.format, value))
  300. cell.value = value
  301. }
  302. // SetColFormat sets the formatting string (Printf) for the specified column
  303. // Update must be called to update the table.
  304. func (t *Table) SetColFormat(id, format string) error {
  305. c := t.header.cmap[id]
  306. if c == nil {
  307. return fmt.Errorf("No column with id:%s", id)
  308. }
  309. c.format = format
  310. return nil
  311. }
  312. // AddRow adds a new row at the end of the table with the specified values
  313. func (t *Table) AddRow(values map[string]interface{}) {
  314. t.InsertRow(len(t.rows), values)
  315. }
  316. // InsertRow inserts the specified values in a new row at the specified index
  317. func (t *Table) InsertRow(row int, values map[string]interface{}) {
  318. t.insertRow(row, values)
  319. t.recalc()
  320. t.Dispatch(OnTableRowCount, nil)
  321. }
  322. // RemoveRow removes from the specified row from the table
  323. func (t *Table) RemoveRow(row int) {
  324. // Checks row index
  325. if row < 0 || row >= len(t.rows) {
  326. panic("Invalid row index")
  327. }
  328. t.removeRow(row)
  329. maxFirst := t.calcMaxFirst()
  330. if t.firstRow > maxFirst {
  331. t.firstRow = maxFirst
  332. }
  333. t.recalc()
  334. t.Dispatch(OnTableRowCount, nil)
  335. }
  336. // Clear removes all rows from the table
  337. func (t *Table) Clear() {
  338. for ri := 0; ri < len(t.rows); ri++ {
  339. trow := t.rows[ri]
  340. trow.DisposeChildren(true)
  341. trow.Dispose()
  342. }
  343. t.rows = nil
  344. t.recalc()
  345. t.Dispatch(OnTableRowCount, nil)
  346. }
  347. // SelectedRow returns the index of the currently selected row
  348. // or -1 if no row selected
  349. func (t *Table) SelectedRow() int {
  350. for ri := 0; ri < len(t.rows); ri++ {
  351. if t.rows[ri].selected {
  352. return ri
  353. }
  354. }
  355. return -1
  356. }
  357. // ShowStatus sets the visibility of the status lines at the bottom of the table
  358. func (t *Table) ShowStatus(show bool) {
  359. if t.statusPanel.Visible() == show {
  360. return
  361. }
  362. t.statusPanel.SetVisible(show)
  363. t.recalcStatus()
  364. t.recalc()
  365. }
  366. // SetStatusText sets the text of status line at the bottom of the table
  367. // It does not change its current visibility
  368. func (t *Table) SetStatusText(text string) {
  369. t.statusLabel.SetText(text)
  370. }
  371. // Rows returns a slice of maps with the contents of the table rows
  372. // specified by the rows first and last index.
  373. // To get all the table rows, use Rows(0, -1)
  374. func (t *Table) Rows(fi, li int) []map[string]interface{} {
  375. if fi < 0 || fi >= len(t.header.cols) {
  376. panic("Invalid first row index")
  377. }
  378. if li < 0 {
  379. li = len(t.rows) - 1
  380. } else if li < 0 || li >= len(t.rows) {
  381. panic("Invalid last row index")
  382. }
  383. if li < fi {
  384. panic("Last index less than first index")
  385. }
  386. res := make([]map[string]interface{}, li-li+1)
  387. for ri := fi; ri <= li; ri++ {
  388. trow := t.rows[ri]
  389. rmap := make(map[string]interface{})
  390. for ci := 0; ci < len(t.header.cols); ci++ {
  391. c := t.header.cols[ci]
  392. rmap[c.id] = trow.cells[c.order].value
  393. }
  394. res = append(res, rmap)
  395. }
  396. return res
  397. }
  398. // Row returns a map with the current contents of the specified row index
  399. func (t *Table) Row(ri int) map[string]interface{} {
  400. if ri < 0 || ri > len(t.header.cols) {
  401. panic("Invalid row index")
  402. }
  403. res := make(map[string]interface{})
  404. trow := t.rows[ri]
  405. for ci := 0; ci < len(t.header.cols); ci++ {
  406. c := t.header.cols[ci]
  407. res[c.id] = trow.cells[c.order].value
  408. }
  409. return res
  410. }
  411. // Cell returns the current content of the specified cell
  412. func (t *Table) Cell(col string, ri int) interface{} {
  413. c := t.header.cmap[col]
  414. if c == nil {
  415. panic("Invalid column id")
  416. }
  417. if ri < 0 || ri >= len(t.rows) {
  418. panic("Invalid row index")
  419. }
  420. trow := t.rows[ri]
  421. return trow.cells[c.order].value
  422. }
  423. // insertRow is the internal version of InsertRow which does not call recalc()
  424. func (t *Table) insertRow(row int, values map[string]interface{}) {
  425. // Checks row index
  426. if row < 0 || row > len(t.rows) {
  427. panic("Invalid row index")
  428. }
  429. // Creates tableRow panel
  430. trow := new(tableRow)
  431. trow.Initialize(0, 0)
  432. trow.cells = make([]*tableCell, 0)
  433. for ci := 0; ci < len(t.header.cols); ci++ {
  434. // Creates tableRow cell panel
  435. cell := new(tableCell)
  436. cell.Initialize(0, 0)
  437. cell.label.initialize("", StyleDefault.Font)
  438. cell.Add(&cell.label)
  439. trow.cells = append(trow.cells, cell)
  440. trow.Panel.Add(cell)
  441. }
  442. t.Panel.Add(trow)
  443. // Inserts tableRow in the table rows at the specified index
  444. t.rows = append(t.rows, nil)
  445. copy(t.rows[row+1:], t.rows[row:])
  446. t.rows[row] = trow
  447. t.updateRowStyle(row)
  448. // Sets the new row values from the specified map
  449. if values != nil {
  450. t.SetRow(row, values)
  451. }
  452. t.recalcRow(row)
  453. }
  454. // ScrollDown scrolls the table the specified number of rows down if possible
  455. func (t *Table) scrollDown(n int, selFirst bool) {
  456. // Calculates number of rows to scroll down
  457. maxFirst := t.calcMaxFirst()
  458. maxScroll := maxFirst - t.firstRow
  459. if maxScroll <= 0 {
  460. return
  461. }
  462. if n > maxScroll {
  463. n = maxScroll
  464. }
  465. t.firstRow += n
  466. if selFirst {
  467. t.selectRow(t.firstRow)
  468. }
  469. t.recalc()
  470. return
  471. }
  472. // ScrollUp scrolls the table the specified number of rows up if possible
  473. func (t *Table) scrollUp(n int, selLast bool) {
  474. // Calculates number of rows to scroll up
  475. if t.firstRow == 0 {
  476. return
  477. }
  478. if n > t.firstRow {
  479. n = t.firstRow
  480. }
  481. t.firstRow -= n
  482. if selLast {
  483. t.selectRow(t.lastRow - n)
  484. }
  485. t.recalc()
  486. }
  487. // removeRow removes from the table the row specified its index
  488. func (t *Table) removeRow(row int) {
  489. // Get row to be removed
  490. trow := t.rows[row]
  491. // Remove row from table
  492. copy(t.rows[row:], t.rows[row+1:])
  493. t.rows[len(t.rows)-1] = nil
  494. t.rows = t.rows[:len(t.rows)-1]
  495. trow.DisposeChildren(true)
  496. trow.Dispose()
  497. // Adjusts table first visible row if necessary
  498. //if t.firstRow == row {
  499. // t.firstRow--
  500. // if t.firstRow < 0 {
  501. // t.firstRow = 0
  502. // }
  503. //}
  504. }
  505. // onMouseEvent process subscribed mouse events
  506. func (t *Table) onMouse(evname string, ev interface{}) {
  507. e := ev.(*window.MouseEvent)
  508. t.root.SetKeyFocus(t)
  509. switch evname {
  510. case OnMouseDown:
  511. // Creates and dispatch TableClickEvent
  512. var tce TableClickEvent
  513. tce.MouseEvent = *e
  514. t.findClick(&tce)
  515. t.Dispatch(OnTableClick, tce)
  516. // Select left clicked row
  517. if tce.Button == window.MouseButtonLeft && tce.Row >= 0 {
  518. t.selectRow(tce.Row)
  519. t.recalc()
  520. }
  521. case OnMouseUp:
  522. default:
  523. return
  524. }
  525. t.root.StopPropagation(StopAll)
  526. }
  527. // onKeyEvent receives subscribed key events for this table
  528. func (t *Table) onKeyEvent(evname string, ev interface{}) {
  529. kev := ev.(*window.KeyEvent)
  530. if kev.Keycode == window.KeyUp && kev.Mods == 0 {
  531. t.selPrev()
  532. } else if kev.Keycode == window.KeyDown && kev.Mods == 0 {
  533. t.selNext()
  534. } else if kev.Keycode == window.KeyPageUp && kev.Mods == 0 {
  535. t.prevPage()
  536. } else if kev.Keycode == window.KeyPageDown && kev.Mods == 0 {
  537. t.nextPage()
  538. } else if kev.Keycode == window.KeyPageUp && kev.Mods == window.ModControl {
  539. t.firstPage()
  540. } else if kev.Keycode == window.KeyPageDown && kev.Mods == window.ModControl {
  541. t.lastPage()
  542. }
  543. }
  544. // onResize receives subscribed resize for this table
  545. func (t *Table) onResize(evname string, ev interface{}) {
  546. t.recalc()
  547. t.recalcStatus()
  548. }
  549. // findClick finds where in the table the specified mouse click event
  550. // occurred updating the specified TableClickEvent with the click coordinates.
  551. func (t *Table) findClick(ev *TableClickEvent) {
  552. x, y := t.ContentCoords(ev.Xpos, ev.Ypos)
  553. ev.X = x
  554. ev.Y = y
  555. ev.Row = -1
  556. // Find column id
  557. colx := float32(0)
  558. for ci := 0; ci < len(t.header.cols); ci++ {
  559. c := t.header.cols[ci]
  560. if !c.Visible() {
  561. continue
  562. }
  563. colx += t.header.cols[ci].Width()
  564. if x < colx {
  565. ev.Col = c.id
  566. break
  567. }
  568. }
  569. // If column not found the user clicked at the right of rows
  570. if ev.Col == "" {
  571. return
  572. }
  573. // Checks if is in header
  574. if t.header.Visible() && y < t.header.Height() {
  575. ev.Header = true
  576. }
  577. // Find row clicked
  578. rowy := float32(0)
  579. if t.header.Visible() {
  580. rowy = t.header.Height()
  581. }
  582. theight := t.ContentHeight()
  583. for ri := t.firstRow; ri < len(t.rows); ri++ {
  584. trow := t.rows[ri]
  585. rowy += trow.height
  586. if rowy > theight {
  587. break
  588. }
  589. if y < rowy {
  590. ev.Row = ri
  591. break
  592. }
  593. }
  594. }
  595. // selNext selects the next row if possible
  596. func (t *Table) selNext() {
  597. // If selected row is last, nothing to do
  598. sel := t.SelectedRow()
  599. if sel == len(t.rows)-1 {
  600. return
  601. }
  602. // If no selected row, selects first visible row
  603. if sel < 0 {
  604. t.selectRow(t.firstRow)
  605. t.recalc()
  606. return
  607. }
  608. // Selects next row
  609. next := sel + 1
  610. t.selectRow(next)
  611. // Scroll down if necessary
  612. if next > t.lastRow {
  613. t.scrollDown(1, false)
  614. } else {
  615. t.recalc()
  616. }
  617. }
  618. // selPrev selects the previous row if possible
  619. func (t *Table) selPrev() {
  620. // If selected row is first, nothing to do
  621. sel := t.SelectedRow()
  622. if sel == 0 {
  623. return
  624. }
  625. // If no selected row, selects last visible row
  626. if sel < 0 {
  627. t.selectRow(t.lastRow)
  628. t.recalc()
  629. return
  630. }
  631. // Selects previous row and selects previous
  632. prev := sel - 1
  633. t.selectRow(prev)
  634. // Scroll up if necessary
  635. if prev < t.firstRow && t.firstRow > 0 {
  636. t.scrollUp(1, false)
  637. } else {
  638. t.recalc()
  639. }
  640. }
  641. // nextPage shows the next page of rows and selects its first row
  642. func (t *Table) nextPage() {
  643. if len(t.rows) == 0 {
  644. return
  645. }
  646. if t.lastRow == len(t.rows)-1 {
  647. t.selectRow(t.lastRow)
  648. t.recalc()
  649. return
  650. }
  651. plen := t.lastRow - t.firstRow
  652. if plen <= 0 {
  653. return
  654. }
  655. t.scrollDown(plen, true)
  656. }
  657. // prevPage shows the previous page of rows and selects its last row
  658. func (t *Table) prevPage() {
  659. if t.firstRow == 0 {
  660. t.selectRow(0)
  661. t.recalc()
  662. return
  663. }
  664. plen := t.lastRow - t.firstRow
  665. if plen <= 0 {
  666. return
  667. }
  668. t.scrollUp(plen, true)
  669. }
  670. // firstPage shows the first page of rows and selects the first row
  671. func (t *Table) firstPage() {
  672. if len(t.rows) == 0 {
  673. return
  674. }
  675. t.firstRow = 0
  676. t.selectRow(0)
  677. t.recalc()
  678. }
  679. // lastPage shows the last page of rows and selects the last row
  680. func (t *Table) lastPage() {
  681. if len(t.rows) == 0 {
  682. return
  683. }
  684. maxFirst := t.calcMaxFirst()
  685. t.firstRow = maxFirst
  686. t.selectRow(len(t.rows) - 1)
  687. t.recalc()
  688. }
  689. // selectRow sets the specified row as selected and unselects all other rows
  690. func (t *Table) selectRow(ri int) {
  691. for i := 0; i < len(t.rows); i++ {
  692. trow := t.rows[i]
  693. if i == ri {
  694. trow.selected = true
  695. t.Dispatch(OnChange, nil)
  696. } else {
  697. trow.selected = false
  698. }
  699. }
  700. }
  701. // recalcHeader recalculates and sets the position and size of the header panels
  702. func (t *Table) recalcHeader() {
  703. posx := float32(0)
  704. height := float32(0)
  705. for ci := 0; ci < len(t.header.cols); ci++ {
  706. c := t.header.cols[ci]
  707. if !c.Visible() {
  708. continue
  709. }
  710. if c.Height() > height {
  711. height = c.Height()
  712. }
  713. c.SetPosition(posx, 0)
  714. c.SetVisible(true)
  715. posx += c.Width()
  716. }
  717. t.header.SetContentSize(posx, height)
  718. }
  719. // recalcStatus recalculates and sets the position and size of the status panel and its label
  720. func (t *Table) recalcStatus() {
  721. if !t.statusPanel.Visible() {
  722. return
  723. }
  724. t.statusPanel.SetContentHeight(t.statusLabel.Height())
  725. py := t.ContentHeight() - t.statusPanel.Height()
  726. t.statusPanel.SetPosition(0, py)
  727. t.statusPanel.SetWidth(t.ContentWidth())
  728. }
  729. // recalc calculates the visibility, positions and sizes of all row cells.
  730. // should be called in the following situations:
  731. // - the table is resized
  732. // - row is added, inserted or removed
  733. // - column alignment and expansion changed
  734. // - column visibility is changed
  735. // - horizontal or vertical scroll position changed
  736. func (t *Table) recalc() {
  737. // Get available row height for rows
  738. starty, theight := t.rowsHeight()
  739. // Determines if it is necessary to show the scrollbar or not.
  740. scroll := false
  741. py := starty
  742. for ri := 0; ri < len(t.rows); ri++ {
  743. trow := t.rows[ri]
  744. py += trow.height
  745. if py > starty+theight {
  746. scroll = true
  747. break
  748. }
  749. }
  750. t.setVScrollBar(scroll)
  751. t.updateVscrollBar()
  752. // Sets the position and sizes of all cells of the visible rows
  753. py = starty
  754. for ri := 0; ri < len(t.rows); ri++ {
  755. trow := t.rows[ri]
  756. // If row is before first row or its y coordinate is greater the table height,
  757. // sets it invisible
  758. if ri < t.firstRow || py > starty+theight {
  759. trow.SetVisible(false)
  760. continue
  761. }
  762. // Set row y position and visible
  763. trow.SetPosition(0, py)
  764. trow.SetVisible(true)
  765. t.updateRowStyle(ri)
  766. // Set the last completely visible row index
  767. if py+trow.Height() <= starty+theight {
  768. t.lastRow = ri
  769. }
  770. //log.Error("ri:%v py:%v theight:%v", ri, py, theight)
  771. py += trow.height
  772. }
  773. // Status panel must be on top of all the row panels
  774. t.SetTopChild(&t.statusPanel)
  775. }
  776. // recalcRow recalculates the positions and sizes of all cells of the specified row
  777. // Should be called when the row is created and column visibility or order is changed.
  778. func (t *Table) recalcRow(ri int) {
  779. trow := t.rows[ri]
  780. // Calculates and sets row height
  781. maxheight := float32(0)
  782. for ci := 0; ci < len(t.header.cols); ci++ {
  783. // If column is hidden, ignore
  784. c := t.header.cols[ci]
  785. if !c.Visible() {
  786. continue
  787. }
  788. cell := trow.cells[c.order]
  789. cellHeight := cell.MinHeight() + cell.label.Height()
  790. if cellHeight > maxheight {
  791. maxheight = cellHeight
  792. }
  793. }
  794. trow.SetContentHeight(maxheight)
  795. // Sets row cells sizes and positions and sets row width
  796. px := float32(0)
  797. for ci := 0; ci < len(t.header.cols); ci++ {
  798. // If column is hidden, ignore
  799. c := t.header.cols[ci]
  800. cell := trow.cells[c.order]
  801. if !c.Visible() {
  802. cell.SetVisible(false)
  803. continue
  804. }
  805. // Sets cell position and size
  806. cell.SetPosition(px, 0)
  807. cell.SetVisible(true)
  808. cell.SetSize(c.Width(), trow.ContentHeight())
  809. // Checks for format function
  810. if c.formatFunc != nil {
  811. text := c.formatFunc(TableCell{t, ri, c.id, cell.value})
  812. cell.label.SetText(text)
  813. }
  814. // Sets the cell label alignment inside the cell
  815. ccw := cell.ContentWidth()
  816. lw := cell.label.Width()
  817. space := ccw - lw
  818. lx := float32(0)
  819. switch c.align {
  820. case AlignLeft:
  821. case AlignRight:
  822. if space > 0 {
  823. lx = ccw - lw
  824. }
  825. case AlignCenter:
  826. if space > 0 {
  827. lx = space / 2
  828. }
  829. }
  830. cell.label.SetPosition(lx, 0)
  831. px += c.Width()
  832. }
  833. trow.SetContentWidth(px)
  834. }
  835. func (t *Table) sortCols() {
  836. }
  837. // rowsHeight returns the available start y coordinate and height in the table for rows,
  838. // considering the visibility of the header and status panels.
  839. func (t *Table) rowsHeight() (float32, float32) {
  840. start := float32(0)
  841. height := t.ContentHeight()
  842. if t.header.Visible() {
  843. height -= t.header.Height()
  844. start += t.header.Height()
  845. }
  846. if t.statusPanel.Visible() {
  847. height -= t.statusPanel.Height()
  848. }
  849. if height < 0 {
  850. return 0, 0
  851. }
  852. return start, height
  853. }
  854. // setVScrollBar sets the visibility state of the vertical scrollbar
  855. func (t *Table) setVScrollBar(state bool) {
  856. // Visible
  857. if state {
  858. var scrollWidth float32 = 20
  859. // Creates scroll bar if necessary
  860. if t.vscroll == nil {
  861. t.vscroll = NewVScrollBar(0, 0)
  862. t.vscroll.SetBorders(0, 0, 0, 1)
  863. t.vscroll.Subscribe(OnChange, t.onVScrollBarEvent)
  864. t.Panel.Add(t.vscroll)
  865. }
  866. // Sets the scroll bar size and positions
  867. py, height := t.rowsHeight()
  868. t.vscroll.SetSize(scrollWidth, height)
  869. t.vscroll.SetPositionX(t.ContentWidth() - scrollWidth)
  870. t.vscroll.SetPositionY(py)
  871. t.vscroll.recalc()
  872. t.vscroll.SetVisible(true)
  873. // Not visible
  874. } else {
  875. if t.vscroll != nil {
  876. t.vscroll.SetVisible(false)
  877. }
  878. }
  879. }
  880. // onVScrollBarEvent is called when a vertical scroll bar event is received
  881. func (t *Table) onVScrollBarEvent(evname string, ev interface{}) {
  882. pos := t.vscroll.Value()
  883. maxFirst := t.calcMaxFirst()
  884. first := int(math.Floor((float64(maxFirst) * pos) + 0.5))
  885. if first == t.firstRow {
  886. return
  887. }
  888. t.firstRow = first
  889. t.recalc()
  890. }
  891. // updateVscrollBar updates the position of the vertical scroll bar button
  892. func (t *Table) updateVscrollBar() {
  893. maxFirst := t.calcMaxFirst()
  894. if t.vscroll != nil && t.vscroll.Visible() {
  895. t.vscroll.SetValue(float32(t.firstRow) / float32(maxFirst))
  896. }
  897. }
  898. // calcMaxFirst calculates the maximum index of the first visible row
  899. // such as the remaing rows fits completely inside the table
  900. // It is used when scrolling the table vertically
  901. func (t *Table) calcMaxFirst() int {
  902. _, total := t.rowsHeight()
  903. ri := len(t.rows) - 1
  904. if ri < 0 {
  905. return 0
  906. }
  907. height := float32(0)
  908. for {
  909. trow := t.rows[ri]
  910. height += trow.height
  911. if height > total {
  912. break
  913. }
  914. ri--
  915. if ri < 0 {
  916. break
  917. }
  918. }
  919. return ri + 1
  920. }
  921. // updateRowStyle applies the correct style for the specified row
  922. func (t *Table) updateRowStyle(ri int) {
  923. row := t.rows[ri]
  924. if row.selected {
  925. t.applyRowStyle(row, &t.styles.Row.Selected)
  926. return
  927. }
  928. t.applyRowStyle(row, &t.styles.Row.Normal)
  929. }
  930. // applyHeaderStyle applies style to the specified table header
  931. func (t *Table) applyHeaderStyle(h *tableColHeader) {
  932. s := t.styles.Header
  933. h.SetBordersFrom(&s.Border)
  934. h.SetBordersColor4(&s.BorderColor)
  935. h.SetPaddingsFrom(&s.Paddings)
  936. h.SetColor(&s.BgColor)
  937. }
  938. // applyRowStyle applies the specified style to all cells for the specified table row
  939. func (t *Table) applyRowStyle(row *tableRow, trs *TableRowStyle) {
  940. for i := 0; i < len(row.cells); i++ {
  941. cell := row.cells[i]
  942. cell.SetBordersFrom(&trs.Border)
  943. cell.SetBordersColor4(&trs.BorderColor)
  944. cell.SetPaddingsFrom(&trs.Paddings)
  945. cell.SetColor(&trs.BgColor)
  946. }
  947. }
  948. // applyStatusStyle applies the status style
  949. func (t *Table) applyStatusStyle() {
  950. s := t.styles.Status
  951. t.statusPanel.SetBordersFrom(&s.Border)
  952. t.statusPanel.SetBordersColor4(&s.BorderColor)
  953. t.statusPanel.SetPaddingsFrom(&s.Paddings)
  954. t.statusPanel.SetColor(&s.BgColor)
  955. }