table.go 33 KB

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