table.go 36 KB

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