table.go 39 KB

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