table.go 30 KB

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