table.go 41 KB

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