table.go 36 KB

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