table.go 42 KB

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