table.go 42 KB

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