table.go 22 KB

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