table.go 28 KB

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