table.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  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. // Event generated when the table is right or left clicked
  13. // Parameter is TableClickEvent
  14. OnTableClick = "onTableClick"
  15. // 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 // Column preferable width in pixels
  38. Hidden bool // Hidden flag
  39. Align Align // Cell content alignment: AlignLeft|AlignCenter|AlignRight
  40. Format string // Format string for numbers and strings
  41. FormatFunc TableFormatFunc // Format function (overrides Format string)
  42. Expand int // Width expansion factor
  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 type 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. t.recalc()
  330. t.Dispatch(OnTableRowCount, nil)
  331. }
  332. // SelectedRow returns the index of the currently selected row
  333. // or -1 if no row selected
  334. func (t *Table) SelectedRow() int {
  335. for ri := 0; ri < len(t.rows); ri++ {
  336. if t.rows[ri].selected {
  337. return ri
  338. }
  339. }
  340. return -1
  341. }
  342. // ShowStatus sets the visibility of the status lines at the bottom of the table
  343. func (t *Table) ShowStatus(show bool) {
  344. if t.statusPanel.Visible() == show {
  345. return
  346. }
  347. t.statusPanel.SetVisible(show)
  348. t.recalcStatus()
  349. t.recalc()
  350. }
  351. // SetStatusText sets the text of status line at the bottom of the table
  352. // It does not change its current visibility
  353. func (t *Table) SetStatusText(text string) {
  354. t.statusLabel.SetText(text)
  355. }
  356. // Rows returns a slice of maps with the contents of the table rows
  357. // specified by the rows first and last index.
  358. // To get all the table rows, use Rows(0, -1)
  359. func (t *Table) Rows(fi, li int) []map[string]interface{} {
  360. if fi < 0 || fi >= len(t.header.cols) {
  361. panic("Invalid first row index")
  362. }
  363. if li < 0 {
  364. li = len(t.rows) - 1
  365. } else if li < 0 || li >= len(t.rows) {
  366. panic("Invalid last row index")
  367. }
  368. if li < fi {
  369. panic("Last index less than first index")
  370. }
  371. res := make([]map[string]interface{}, li-li+1)
  372. for ri := fi; ri <= li; ri++ {
  373. trow := t.rows[ri]
  374. rmap := make(map[string]interface{})
  375. for ci := 0; ci < len(t.header.cols); ci++ {
  376. c := t.header.cols[ci]
  377. rmap[c.id] = trow.cells[c.order].value
  378. }
  379. res = append(res, rmap)
  380. }
  381. return res
  382. }
  383. // Row returns a map with the current contents of the specified row index
  384. func (t *Table) Row(ri int) map[string]interface{} {
  385. if ri < 0 || ri > len(t.header.cols) {
  386. panic("Invalid row index")
  387. }
  388. res := make(map[string]interface{})
  389. trow := t.rows[ri]
  390. for ci := 0; ci < len(t.header.cols); ci++ {
  391. c := t.header.cols[ci]
  392. res[c.id] = trow.cells[c.order].value
  393. }
  394. return res
  395. }
  396. // Cell returns the current content of the specified cell
  397. func (t *Table) Cell(col string, ri int) interface{} {
  398. c := t.header.cmap[col]
  399. if c == nil {
  400. panic("Invalid column id")
  401. }
  402. if ri < 0 || ri >= len(t.rows) {
  403. panic("Invalid row index")
  404. }
  405. trow := t.rows[ri]
  406. return trow.cells[c.order].value
  407. }
  408. // insertRow is the internal version of InsertRow which does not call recalc()
  409. func (t *Table) insertRow(row int, values map[string]interface{}) {
  410. // Checks row index
  411. if row < 0 || row > len(t.rows) {
  412. panic("Invalid row index")
  413. }
  414. // Creates tableRow panel
  415. trow := new(tableRow)
  416. trow.Initialize(0, 0)
  417. trow.cells = make([]*tableCell, 0)
  418. for ci := 0; ci < len(t.header.cols); ci++ {
  419. // Creates tableRow cell panel
  420. cell := new(tableCell)
  421. cell.Initialize(0, 0)
  422. cell.label.initialize("", StyleDefault.Font)
  423. cell.Add(&cell.label)
  424. trow.cells = append(trow.cells, cell)
  425. trow.Panel.Add(cell)
  426. }
  427. t.Panel.Add(trow)
  428. // Inserts tableRow in the table rows at the specified index
  429. t.rows = append(t.rows, nil)
  430. copy(t.rows[row+1:], t.rows[row:])
  431. t.rows[row] = trow
  432. t.updateRowStyle(row)
  433. // Sets the new row values from the specified map
  434. if values != nil {
  435. t.SetRow(row, values)
  436. }
  437. t.recalcRow(row)
  438. }
  439. // ScrollDown scrolls the table the specified number of rows down if possible
  440. func (t *Table) scrollDown(n int, selFirst bool) {
  441. // Calculates number of rows to scroll down
  442. maxFirst := t.calcMaxFirst()
  443. maxScroll := maxFirst - t.firstRow
  444. if maxScroll <= 0 {
  445. return
  446. }
  447. if n > maxScroll {
  448. n = maxScroll
  449. }
  450. t.firstRow += n
  451. // Update scroll bar if visible
  452. if t.vscroll != nil && t.vscroll.Visible() {
  453. t.vscroll.SetValue(float32(t.firstRow) / float32(maxFirst))
  454. }
  455. if selFirst {
  456. t.selectRow(t.firstRow)
  457. }
  458. t.recalc()
  459. return
  460. }
  461. // ScrollUp scrolls the table the specified number of rows up if possible
  462. func (t *Table) scrollUp(n int, selLast bool) {
  463. // Calculates number of rows to scroll up
  464. if t.firstRow == 0 {
  465. return
  466. }
  467. if n > t.firstRow {
  468. n = t.firstRow
  469. }
  470. t.firstRow -= n
  471. // Update scroll bar if visible
  472. if t.vscroll != nil && t.vscroll.Visible() {
  473. t.vscroll.SetValue(float32(t.firstRow) / float32(t.calcMaxFirst()))
  474. }
  475. if selLast {
  476. t.selectRow(t.lastRow - n)
  477. }
  478. t.recalc()
  479. }
  480. // removeRow removes from the table the row specified its index
  481. func (t *Table) removeRow(row int) {
  482. // Get row to be removed
  483. trow := t.rows[row]
  484. // Remove row from table
  485. copy(t.rows[row:], t.rows[row+1:])
  486. t.rows[len(t.rows)-1] = nil
  487. t.rows = t.rows[:len(t.rows)-1]
  488. // Dispose the row cell panels and its children
  489. for i := 0; i < len(trow.cells); i++ {
  490. cell := trow.cells[i]
  491. cell.DisposeChildren(true)
  492. cell.Dispose()
  493. }
  494. // Adjusts table first visible row if necessary
  495. //if t.firstRow == row {
  496. // t.firstRow--
  497. // if t.firstRow < 0 {
  498. // t.firstRow = 0
  499. // }
  500. //}
  501. }
  502. // onMouseEvent process subscribed mouse events
  503. func (t *Table) onMouse(evname string, ev interface{}) {
  504. e := ev.(*window.MouseEvent)
  505. t.root.SetKeyFocus(t)
  506. switch evname {
  507. case OnMouseDown:
  508. // Creates and dispatch TableClickEvent
  509. var tce TableClickEvent
  510. tce.MouseEvent = *e
  511. t.findClick(&tce)
  512. t.Dispatch(OnTableClick, tce)
  513. // Select left clicked row
  514. if tce.Button == window.MouseButtonLeft && tce.Row >= 0 {
  515. t.selectRow(tce.Row)
  516. t.recalc()
  517. }
  518. case OnMouseUp:
  519. default:
  520. return
  521. }
  522. t.root.StopPropagation(StopAll)
  523. }
  524. // onKeyEvent receives subscribed key events for this table
  525. func (t *Table) onKeyEvent(evname string, ev interface{}) {
  526. kev := ev.(*window.KeyEvent)
  527. switch kev.Keycode {
  528. case window.KeyUp:
  529. t.selPrev()
  530. case window.KeyDown:
  531. t.selNext()
  532. case window.KeyPageUp:
  533. t.prevPage()
  534. case window.KeyPageDown:
  535. t.nextPage()
  536. }
  537. }
  538. // onResize receives subscribed resize for this table
  539. func (t *Table) onResize(evname string, ev interface{}) {
  540. t.recalc()
  541. t.recalcStatus()
  542. }
  543. // findClick finds where in the table the specified mouse click event
  544. // occurred updating the specified TableClickEvent with the click coordinates.
  545. func (t *Table) findClick(ev *TableClickEvent) {
  546. x, y := t.ContentCoords(ev.Xpos, ev.Ypos)
  547. ev.X = x
  548. ev.Y = y
  549. ev.Row = -1
  550. // Find column id
  551. colx := float32(0)
  552. for ci := 0; ci < len(t.header.cols); ci++ {
  553. c := t.header.cols[ci]
  554. if !c.Visible() {
  555. continue
  556. }
  557. colx += t.header.cols[ci].Width()
  558. if x < colx {
  559. ev.Col = c.id
  560. break
  561. }
  562. }
  563. // If column not found the user clicked at the right of rows
  564. if ev.Col == "" {
  565. return
  566. }
  567. // Checks if is in header
  568. if t.header.Visible() && y < t.header.Height() {
  569. ev.Header = true
  570. }
  571. // Find row clicked
  572. rowy := float32(0)
  573. if t.header.Visible() {
  574. rowy = t.header.Height()
  575. }
  576. theight := t.ContentHeight()
  577. for ri := t.firstRow; ri < len(t.rows); ri++ {
  578. trow := t.rows[ri]
  579. rowy += trow.height
  580. if rowy > theight {
  581. break
  582. }
  583. if y < rowy {
  584. ev.Row = ri
  585. break
  586. }
  587. }
  588. }
  589. // selNext selects the next row if possible
  590. func (t *Table) selNext() {
  591. // If selected row is last, nothing to do
  592. sel := t.SelectedRow()
  593. if sel == len(t.rows)-1 {
  594. return
  595. }
  596. // If no selected row, selects first visible row
  597. if sel < 0 {
  598. t.selectRow(t.firstRow)
  599. t.recalc()
  600. return
  601. }
  602. // Selects next row
  603. next := sel + 1
  604. t.selectRow(next)
  605. // Scroll down if necessary
  606. if next > t.lastRow {
  607. t.scrollDown(1, false)
  608. } else {
  609. t.recalc()
  610. }
  611. }
  612. // selPrev selects the previous row if possible
  613. func (t *Table) selPrev() {
  614. // If selected row is first, nothing to do
  615. sel := t.SelectedRow()
  616. if sel == 0 {
  617. return
  618. }
  619. // If no selected row, selects last visible row
  620. if sel < 0 {
  621. t.selectRow(t.lastRow)
  622. t.recalc()
  623. return
  624. }
  625. // Selects previous row and selects previous
  626. prev := sel - 1
  627. t.selectRow(prev)
  628. // Scroll up if necessary
  629. if prev < t.firstRow && t.firstRow > 0 {
  630. t.scrollUp(1, false)
  631. } else {
  632. t.recalc()
  633. }
  634. }
  635. // nextPage increments the first visible row to show next page of rows
  636. func (t *Table) nextPage() {
  637. if t.lastRow == len(t.rows)-1 {
  638. return
  639. }
  640. plen := t.lastRow - t.firstRow
  641. if plen <= 0 {
  642. return
  643. }
  644. t.scrollDown(plen, true)
  645. }
  646. // prevPage advances the first visible row
  647. func (t *Table) prevPage() {
  648. if t.firstRow == 0 {
  649. return
  650. }
  651. plen := t.lastRow - t.firstRow
  652. if plen <= 0 {
  653. return
  654. }
  655. t.scrollUp(plen, true)
  656. }
  657. // selectRow sets the specified row as selected and unselects all other rows
  658. func (t *Table) selectRow(ri int) {
  659. for i := 0; i < len(t.rows); i++ {
  660. trow := t.rows[i]
  661. if i == ri {
  662. trow.selected = true
  663. t.Dispatch(OnChange, nil)
  664. } else {
  665. trow.selected = false
  666. }
  667. }
  668. }
  669. // recalcHeader recalculates and sets the position and size of the header panels
  670. func (t *Table) recalcHeader() {
  671. posx := float32(0)
  672. height := float32(0)
  673. for ci := 0; ci < len(t.header.cols); ci++ {
  674. c := t.header.cols[ci]
  675. if !c.Visible() {
  676. continue
  677. }
  678. if c.Height() > height {
  679. height = c.Height()
  680. }
  681. c.SetPosition(posx, 0)
  682. c.SetVisible(true)
  683. posx += c.Width()
  684. }
  685. t.header.SetContentSize(posx, height)
  686. }
  687. // recalcStatus recalculates and sets the position and size of the status panel and its label
  688. func (t *Table) recalcStatus() {
  689. if !t.statusPanel.Visible() {
  690. return
  691. }
  692. t.statusPanel.SetContentHeight(t.statusLabel.Height())
  693. py := t.ContentHeight() - t.statusPanel.Height()
  694. t.statusPanel.SetPosition(0, py)
  695. t.statusPanel.SetWidth(t.ContentWidth())
  696. }
  697. // recalc calculates the visibility, positions and sizes of all row cells.
  698. // should be called in the following situations:
  699. // - the table is resized
  700. // - row is added, inserted or removed
  701. // - column alignment and expansion changed
  702. // - column visibility is changed
  703. // - horizontal or vertical scroll position changed
  704. func (t *Table) recalc() {
  705. // Get available row height for rows
  706. starty, theight := t.rowsHeight()
  707. // Determines if it is necessary to show the scrollbar or not.
  708. scroll := false
  709. py := starty
  710. for ri := 0; ri < len(t.rows); ri++ {
  711. trow := t.rows[ri]
  712. py += trow.height
  713. if py > starty+theight {
  714. scroll = true
  715. break
  716. }
  717. }
  718. t.setVScrollBar(scroll)
  719. // Sets the position and sizes of all cells of the visible rows
  720. py = starty
  721. for ri := 0; ri < len(t.rows); ri++ {
  722. trow := t.rows[ri]
  723. // If row is before first row or its y coordinate is greater the table height,
  724. // sets it invisible
  725. if ri < t.firstRow || py > starty+theight {
  726. trow.SetVisible(false)
  727. continue
  728. }
  729. // Set row y position and visible
  730. trow.SetPosition(0, py)
  731. trow.SetVisible(true)
  732. t.updateRowStyle(ri)
  733. // Set the last completely visible row index
  734. if py+trow.Height() <= starty+theight {
  735. t.lastRow = ri
  736. }
  737. //log.Error("ri:%v py:%v theight:%v", ri, py, theight)
  738. py += trow.height
  739. }
  740. t.SetTopChild(&t.statusPanel)
  741. }
  742. // recalcRow recalculates the positions and sizes of all cells of the specified row
  743. // Should be called when the row is created and column visibility or order is changed.
  744. func (t *Table) recalcRow(ri int) {
  745. trow := t.rows[ri]
  746. // Calculates and sets row height
  747. maxheight := float32(0)
  748. for ci := 0; ci < len(t.header.cols); ci++ {
  749. // If column is hidden, ignore
  750. c := t.header.cols[ci]
  751. if !c.Visible() {
  752. continue
  753. }
  754. cell := trow.cells[c.order]
  755. cellHeight := cell.MinHeight() + cell.label.Height()
  756. if cellHeight > maxheight {
  757. maxheight = cellHeight
  758. }
  759. }
  760. trow.SetContentHeight(maxheight)
  761. // Sets row cells sizes and positions and sets row width
  762. px := float32(0)
  763. for ci := 0; ci < len(t.header.cols); ci++ {
  764. // If column is hidden, ignore
  765. c := t.header.cols[ci]
  766. cell := trow.cells[c.order]
  767. if !c.Visible() {
  768. cell.SetVisible(false)
  769. continue
  770. }
  771. // Sets cell position and size
  772. cell.SetPosition(px, 0)
  773. cell.SetVisible(true)
  774. cell.SetSize(c.Width(), trow.ContentHeight())
  775. // Checks for format function
  776. if c.formatFunc != nil {
  777. text := c.formatFunc(TableCell{t, ri, c.id, cell.value})
  778. cell.label.SetText(text)
  779. }
  780. // Sets the cell label alignment inside the cell
  781. ccw := cell.ContentWidth()
  782. lw := cell.label.Width()
  783. space := ccw - lw
  784. lx := float32(0)
  785. switch c.align {
  786. case AlignLeft:
  787. case AlignRight:
  788. if space > 0 {
  789. lx = ccw - lw
  790. }
  791. case AlignCenter:
  792. if space > 0 {
  793. lx = space / 2
  794. }
  795. }
  796. cell.label.SetPosition(lx, 0)
  797. px += c.Width()
  798. }
  799. trow.SetContentWidth(px)
  800. }
  801. func (t *Table) sortCols() {
  802. }
  803. // rowsHeight returns the available start y coordinate and height in the table for rows,
  804. // considering the visibility of the header and status panels.
  805. func (t *Table) rowsHeight() (float32, float32) {
  806. start := float32(0)
  807. height := t.ContentHeight()
  808. if t.header.Visible() {
  809. height -= t.header.Height()
  810. start += t.header.Height()
  811. }
  812. if t.statusPanel.Visible() {
  813. height -= t.statusPanel.Height()
  814. }
  815. if height < 0 {
  816. return 0, 0
  817. }
  818. return start, height
  819. }
  820. // setVScrollBar sets the visibility state of the vertical scrollbar
  821. func (t *Table) setVScrollBar(state bool) {
  822. // Visible
  823. if state {
  824. var scrollWidth float32 = 20
  825. // Creates scroll bar if necessary
  826. if t.vscroll == nil {
  827. t.vscroll = NewVScrollBar(0, 0)
  828. t.vscroll.SetBorders(0, 0, 0, 1)
  829. t.vscroll.Subscribe(OnChange, t.onVScrollBarEvent)
  830. t.Panel.Add(t.vscroll)
  831. }
  832. // Sets the scroll bar size and positions
  833. py, height := t.rowsHeight()
  834. t.vscroll.SetSize(scrollWidth, height)
  835. t.vscroll.SetPositionX(t.ContentWidth() - scrollWidth)
  836. t.vscroll.SetPositionY(py)
  837. t.vscroll.recalc()
  838. t.vscroll.SetVisible(true)
  839. // Not visible
  840. } else {
  841. if t.vscroll != nil {
  842. t.vscroll.SetVisible(false)
  843. }
  844. }
  845. }
  846. // onVScrollBarEvent is called when a vertical scroll bar event is received
  847. func (t *Table) onVScrollBarEvent(evname string, ev interface{}) {
  848. pos := t.vscroll.Value()
  849. maxFirst := t.calcMaxFirst()
  850. first := int(math.Floor((float64(maxFirst) * pos) + 0.5))
  851. if first == t.firstRow {
  852. return
  853. }
  854. t.firstRow = first
  855. t.recalc()
  856. }
  857. // calcMaxFirst calculates the maximum index of the first visible row
  858. // such as the remaing rows fits completely inside the table
  859. // It is used when scrolling the table vertically
  860. func (t *Table) calcMaxFirst() int {
  861. _, total := t.rowsHeight()
  862. ri := len(t.rows) - 1
  863. if ri < 0 {
  864. return 0
  865. }
  866. height := float32(0)
  867. for {
  868. trow := t.rows[ri]
  869. height += trow.height
  870. if height > total {
  871. break
  872. }
  873. ri--
  874. if ri < 0 {
  875. break
  876. }
  877. }
  878. return ri + 1
  879. }
  880. // updateRowStyle applies the correct style for the specified row
  881. func (t *Table) updateRowStyle(ri int) {
  882. row := t.rows[ri]
  883. if row.selected {
  884. t.applyRowStyle(row, &t.styles.Row.Selected)
  885. return
  886. }
  887. t.applyRowStyle(row, &t.styles.Row.Normal)
  888. }
  889. // applyHeaderStyle applies style to the specified table header
  890. func (t *Table) applyHeaderStyle(h *tableColHeader) {
  891. s := t.styles.Header
  892. h.SetBordersFrom(&s.Border)
  893. h.SetBordersColor4(&s.BorderColor)
  894. h.SetPaddingsFrom(&s.Paddings)
  895. h.SetColor(&s.BgColor)
  896. }
  897. // applyRowStyle applies the specified style to all cells for the specified table row
  898. func (t *Table) applyRowStyle(row *tableRow, trs *TableRowStyle) {
  899. for i := 0; i < len(row.cells); i++ {
  900. cell := row.cells[i]
  901. cell.SetBordersFrom(&trs.Border)
  902. cell.SetBordersColor4(&trs.BorderColor)
  903. cell.SetPaddingsFrom(&trs.Paddings)
  904. cell.SetColor(&trs.BgColor)
  905. }
  906. }
  907. // applyStatusStyle applies the status style
  908. func (t *Table) applyStatusStyle() {
  909. s := t.styles.Status
  910. t.statusPanel.SetBordersFrom(&s.Border)
  911. t.statusPanel.SetBordersColor4(&s.BorderColor)
  912. t.statusPanel.SetPaddingsFrom(&s.Paddings)
  913. t.statusPanel.SetColor(&s.BgColor)
  914. }