table.go 41 KB

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