table.go 41 KB

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