table.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. package widget
  2. import (
  3. "fyne.io/fyne/v2"
  4. "fyne.io/fyne/v2/canvas"
  5. "fyne.io/fyne/v2/driver/desktop"
  6. "fyne.io/fyne/v2/internal/widget"
  7. "fyne.io/fyne/v2/theme"
  8. )
  9. // Declare conformity with Widget interface.
  10. var _ fyne.Widget = (*Table)(nil)
  11. // TableCellID is a type that represents a cell's position in a table based on it's row and column location.
  12. type TableCellID struct {
  13. Row int
  14. Col int
  15. }
  16. // Table widget is a grid of items that can be scrolled and a cell selected.
  17. // Its performance is provided by caching cell templates created with CreateCell and re-using them with UpdateCell.
  18. // The size of the content rows/columns is returned by the Length callback.
  19. //
  20. // Since: 1.4
  21. type Table struct {
  22. BaseWidget
  23. Length func() (int, int) `json:"-"`
  24. CreateCell func() fyne.CanvasObject `json:"-"`
  25. UpdateCell func(id TableCellID, template fyne.CanvasObject) `json:"-"`
  26. OnSelected func(id TableCellID) `json:"-"`
  27. OnUnselected func(id TableCellID) `json:"-"`
  28. selectedCell, hoveredCell *TableCellID
  29. cells *tableCells
  30. columnWidths, rowHeights map[int]float32
  31. moveCallback func()
  32. offset fyne.Position
  33. scroll *widget.Scroll
  34. }
  35. // NewTable returns a new performant table widget defined by the passed functions.
  36. // The first returns the data size in rows and columns, second parameter is a function that returns cell
  37. // template objects that can be cached and the third is used to apply data at specified data location to the
  38. // passed template CanvasObject.
  39. //
  40. // Since: 1.4
  41. func NewTable(length func() (int, int), create func() fyne.CanvasObject, update func(TableCellID, fyne.CanvasObject)) *Table {
  42. t := &Table{Length: length, CreateCell: create, UpdateCell: update}
  43. t.ExtendBaseWidget(t)
  44. return t
  45. }
  46. // CreateRenderer returns a new renderer for the table.
  47. //
  48. // Implements: fyne.Widget
  49. func (t *Table) CreateRenderer() fyne.WidgetRenderer {
  50. t.ExtendBaseWidget(t)
  51. marker := canvas.NewRectangle(theme.SelectionColor())
  52. hover := canvas.NewRectangle(theme.HoverColor())
  53. cellSize := t.templateSize()
  54. t.cells = newTableCells(t, cellSize)
  55. t.scroll = widget.NewScroll(t.cells)
  56. obj := []fyne.CanvasObject{marker, hover, t.scroll}
  57. r := &tableRenderer{t: t, scroll: t.scroll, marker: marker, hover: hover, cellSize: cellSize}
  58. r.SetObjects(obj)
  59. t.moveCallback = r.moveIndicators
  60. t.scroll.OnScrolled = func(pos fyne.Position) {
  61. t.offset = pos
  62. t.cells.Refresh()
  63. r.moveIndicators()
  64. }
  65. r.Layout(t.Size())
  66. return r
  67. }
  68. // Select will mark the specified cell as selected.
  69. func (t *Table) Select(id TableCellID) {
  70. if t.Length == nil {
  71. return
  72. }
  73. rows, cols := t.Length()
  74. if id.Row >= rows || id.Col >= cols {
  75. return
  76. }
  77. if t.selectedCell != nil && *t.selectedCell == id {
  78. return
  79. }
  80. if f := t.OnUnselected; f != nil && t.selectedCell != nil {
  81. f(*t.selectedCell)
  82. }
  83. t.selectedCell = &id
  84. t.ScrollTo(id)
  85. if f := t.OnSelected; f != nil {
  86. f(id)
  87. }
  88. }
  89. // SetColumnWidth supports changing the width of the specified column. Columns normally take the width of the template
  90. // cell returned from the CreateCell callback. The width parameter uses the same units as a fyne.Size type and refers
  91. // to the internal content width not including the divider size.
  92. //
  93. // Since: 1.4.1
  94. func (t *Table) SetColumnWidth(id int, width float32) {
  95. if t.columnWidths == nil {
  96. t.columnWidths = make(map[int]float32)
  97. }
  98. t.columnWidths[id] = width
  99. t.Refresh()
  100. t.scroll.Refresh()
  101. }
  102. // SetRowHeight supports changing the height of the specified row. Rows normally take the height of the template
  103. // cell returned from the CreateCell callback. The height parameter uses the same units as a fyne.Size type and refers
  104. // to the internal content height not including the divider size.
  105. //
  106. // Since: 2.3
  107. func (t *Table) SetRowHeight(id int, height float32) {
  108. if t.rowHeights == nil {
  109. t.rowHeights = make(map[int]float32)
  110. }
  111. t.rowHeights[id] = height
  112. t.Refresh()
  113. t.scroll.Refresh()
  114. }
  115. // Unselect will mark the cell provided by id as unselected.
  116. func (t *Table) Unselect(id TableCellID) {
  117. if t.selectedCell == nil || id != *t.selectedCell {
  118. return
  119. }
  120. t.selectedCell = nil
  121. if t.moveCallback != nil {
  122. t.moveCallback()
  123. }
  124. if f := t.OnUnselected; f != nil {
  125. f(id)
  126. }
  127. }
  128. // UnselectAll will mark all cells as unselected.
  129. //
  130. // Since: 2.1
  131. func (t *Table) UnselectAll() {
  132. if t.selectedCell == nil {
  133. return
  134. }
  135. selected := *t.selectedCell
  136. t.selectedCell = nil
  137. if t.moveCallback != nil {
  138. t.moveCallback()
  139. }
  140. if f := t.OnUnselected; f != nil {
  141. f(selected)
  142. }
  143. }
  144. // ScrollTo will scroll to the given cell without changing the selection.
  145. // Attempting to scroll beyond the limits of the table will scroll to
  146. // the edge of the table instead.
  147. //
  148. // Since: 2.1
  149. func (t *Table) ScrollTo(id TableCellID) {
  150. if t.Length == nil {
  151. return
  152. }
  153. if t.scroll == nil {
  154. return
  155. }
  156. rows, cols := t.Length()
  157. if id.Row >= rows {
  158. id.Row = rows - 1
  159. }
  160. if id.Col >= cols {
  161. id.Col = cols - 1
  162. }
  163. scrollPos := t.offset
  164. cellX, cellWidth := t.findX(id.Col)
  165. if cellX < scrollPos.X {
  166. scrollPos.X = cellX
  167. } else if cellX+cellWidth > scrollPos.X+t.scroll.Size().Width {
  168. scrollPos.X = cellX + cellWidth - t.scroll.Size().Width
  169. }
  170. cellY, cellHeight := t.findY(id.Row)
  171. if cellY < scrollPos.Y {
  172. scrollPos.Y = cellY
  173. } else if cellY+cellHeight > scrollPos.Y+t.scroll.Size().Height {
  174. scrollPos.Y = cellY + cellHeight - t.scroll.Size().Height
  175. }
  176. t.scroll.Offset = scrollPos
  177. t.offset = scrollPos
  178. t.finishScroll()
  179. }
  180. // ScrollToBottom scrolls to the last row in the table
  181. //
  182. // Since: 2.1
  183. func (t *Table) ScrollToBottom() {
  184. if t.Length == nil || t.scroll == nil {
  185. return
  186. }
  187. rows, _ := t.Length()
  188. cellY, cellHeight := t.findY(rows - 1)
  189. y := cellY + cellHeight - t.scroll.Size().Height
  190. if y <= 0 {
  191. return
  192. }
  193. t.scroll.Offset.Y = y
  194. t.offset.Y = y
  195. t.finishScroll()
  196. }
  197. // ScrollToLeading scrolls horizontally to the leading edge of the table
  198. //
  199. // Since: 2.1
  200. func (t *Table) ScrollToLeading() {
  201. if t.scroll == nil {
  202. return
  203. }
  204. t.scroll.Offset.X = 0
  205. t.offset.X = 0
  206. t.finishScroll()
  207. }
  208. // ScrollToTop scrolls to the first row in the table
  209. //
  210. // Since: 2.1
  211. func (t *Table) ScrollToTop() {
  212. if t.scroll == nil {
  213. return
  214. }
  215. t.scroll.Offset.Y = 0
  216. t.offset.Y = 0
  217. t.finishScroll()
  218. }
  219. // ScrollToTrailing scrolls horizontally to the trailing edge of the table
  220. //
  221. // Since: 2.1
  222. func (t *Table) ScrollToTrailing() {
  223. if t.scroll == nil || t.Length == nil {
  224. return
  225. }
  226. _, cols := t.Length()
  227. cellX, cellWidth := t.findX(cols - 1)
  228. scrollX := cellX + cellWidth - t.scroll.Size().Width
  229. if scrollX <= 0 {
  230. return
  231. }
  232. t.scroll.Offset.X = scrollX
  233. t.offset.X = scrollX
  234. t.finishScroll()
  235. }
  236. func (t *Table) findX(col int) (cellX float32, cellWidth float32) {
  237. cellSize := t.templateSize()
  238. for i := 0; i <= col; i++ {
  239. if cellWidth > 0 {
  240. cellX += cellWidth + theme.Padding()
  241. }
  242. width := cellSize.Width
  243. if w, ok := t.columnWidths[i]; ok {
  244. width = w
  245. }
  246. cellWidth = width
  247. }
  248. return
  249. }
  250. func (t *Table) findY(row int) (cellY float32, cellHeight float32) {
  251. cellSize := t.templateSize()
  252. for i := 0; i <= row; i++ {
  253. if cellHeight > 0 {
  254. cellY += cellHeight + theme.Padding()
  255. }
  256. height := cellSize.Height
  257. if h, ok := t.rowHeights[i]; ok {
  258. height = h
  259. }
  260. cellHeight = height
  261. }
  262. return
  263. }
  264. func (t *Table) finishScroll() {
  265. if t.moveCallback != nil {
  266. t.moveCallback()
  267. }
  268. t.scroll.Refresh()
  269. t.cells.Refresh()
  270. }
  271. func (t *Table) templateSize() fyne.Size {
  272. if f := t.CreateCell; f != nil {
  273. template := f() // don't use cache, we need new template
  274. return template.MinSize()
  275. }
  276. fyne.LogError("Missing CreateCell callback required for Table", nil)
  277. return fyne.Size{}
  278. }
  279. func (t *Table) visibleColumnWidths(colWidth float32, cols int) (visible map[int]float32, offX float32, minCol, maxCol int) {
  280. maxCol = cols
  281. colOffset := float32(0)
  282. isVisible := false
  283. visible = make(map[int]float32)
  284. if t.scroll.Size().Width <= 0 {
  285. return
  286. }
  287. for i := 0; i < cols; i++ {
  288. width := colWidth
  289. if w, ok := t.columnWidths[i]; ok {
  290. width = w
  291. }
  292. if colOffset <= t.offset.X-width-theme.Padding() {
  293. // before scroll
  294. } else if colOffset <= t.offset.X {
  295. minCol = i
  296. offX = colOffset
  297. isVisible = true
  298. }
  299. if colOffset < t.offset.X+t.scroll.Size().Width {
  300. maxCol = i + 1
  301. } else {
  302. break
  303. }
  304. colOffset += width + theme.Padding()
  305. if isVisible {
  306. visible[i] = width
  307. }
  308. }
  309. return
  310. }
  311. func (t *Table) visibleRowHeights(rowHeight float32, rows int) (visible map[int]float32, offY float32, minRow, maxRow int) {
  312. maxRow = rows
  313. rowOffset := float32(0)
  314. isVisible := false
  315. visible = make(map[int]float32)
  316. if t.scroll.Size().Height <= 0 {
  317. return
  318. }
  319. for i := 0; i < rows; i++ {
  320. height := rowHeight
  321. if h, ok := t.rowHeights[i]; ok {
  322. height = h
  323. }
  324. if rowOffset <= t.offset.Y-height-theme.Padding() {
  325. // before scroll
  326. } else if rowOffset <= t.offset.Y {
  327. minRow = i
  328. offY = rowOffset
  329. isVisible = true
  330. }
  331. if rowOffset < t.offset.Y+t.scroll.Size().Height {
  332. maxRow = i + 1
  333. } else {
  334. break
  335. }
  336. rowOffset += height + theme.Padding()
  337. if isVisible {
  338. visible[i] = height
  339. }
  340. }
  341. return
  342. }
  343. // Declare conformity with WidgetRenderer interface.
  344. var _ fyne.WidgetRenderer = (*tableRenderer)(nil)
  345. type tableRenderer struct {
  346. widget.BaseRenderer
  347. t *Table
  348. scroll *widget.Scroll
  349. hover, marker *canvas.Rectangle
  350. dividers []fyne.CanvasObject
  351. cellSize fyne.Size
  352. }
  353. func (t *tableRenderer) Layout(s fyne.Size) {
  354. t.scroll.Resize(s)
  355. t.moveIndicators()
  356. }
  357. func (t *tableRenderer) MinSize() fyne.Size {
  358. return t.t.scroll.MinSize().Max(t.cellSize)
  359. }
  360. func (t *tableRenderer) Refresh() {
  361. t.cellSize = t.t.templateSize()
  362. t.moveIndicators()
  363. t.marker.FillColor = theme.SelectionColor()
  364. t.marker.Refresh()
  365. t.hover.FillColor = theme.HoverColor()
  366. t.hover.Refresh()
  367. t.t.cells.Refresh()
  368. }
  369. func (t *tableRenderer) moveIndicators() {
  370. rows, cols := 0, 0
  371. if f := t.t.Length; f != nil {
  372. rows, cols = t.t.Length()
  373. }
  374. visibleColWidths, offX, minCol, maxCol := t.t.visibleColumnWidths(t.cellSize.Width, cols)
  375. visibleRowHeights, offY, minRow, maxRow := t.t.visibleRowHeights(t.cellSize.Height, rows)
  376. separatorThickness := theme.SeparatorThicknessSize()
  377. dividerOff := (theme.Padding() - separatorThickness) / 2
  378. if t.t.selectedCell == nil {
  379. t.moveMarker(t.marker, -1, -1, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  380. } else {
  381. t.moveMarker(t.marker, t.t.selectedCell.Row, t.t.selectedCell.Col, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  382. }
  383. if t.t.hoveredCell == nil {
  384. t.moveMarker(t.hover, -1, -1, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  385. } else {
  386. t.moveMarker(t.hover, t.t.hoveredCell.Row, t.t.hoveredCell.Col, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  387. }
  388. colDivs := maxCol - minCol - 1
  389. rowDivs := maxRow - minRow - 1
  390. if len(t.dividers) < colDivs+rowDivs {
  391. for i := len(t.dividers); i < colDivs+rowDivs; i++ {
  392. t.dividers = append(t.dividers, NewSeparator())
  393. }
  394. obj := []fyne.CanvasObject{t.marker, t.hover}
  395. obj = append(obj, t.dividers...)
  396. t.SetObjects(append(obj, t.scroll))
  397. }
  398. divs := 0
  399. i := minCol
  400. for x := offX + visibleColWidths[i]; i < minCol+colDivs && divs < len(t.dividers); x += visibleColWidths[i] + theme.Padding() {
  401. i++
  402. t.dividers[divs].Move(fyne.NewPos(x-t.scroll.Offset.X+dividerOff, 0))
  403. t.dividers[divs].Resize(fyne.NewSize(separatorThickness, t.t.size.Height))
  404. t.dividers[divs].Show()
  405. divs++
  406. }
  407. i = minRow
  408. for y := offY + visibleRowHeights[i]; i < minRow+rowDivs && divs < len(t.dividers); y += visibleRowHeights[i] + theme.Padding() {
  409. i++
  410. t.dividers[divs].Move(fyne.NewPos(0, y-t.scroll.Offset.Y+dividerOff))
  411. t.dividers[divs].Resize(fyne.NewSize(t.t.size.Width, separatorThickness))
  412. t.dividers[divs].Show()
  413. divs++
  414. }
  415. for i := divs; i < len(t.dividers); i++ {
  416. t.dividers[i].Hide()
  417. }
  418. canvas.Refresh(t.t)
  419. }
  420. func (t *tableRenderer) moveMarker(marker fyne.CanvasObject, row, col int, offX, offY float32, minCol, minRow int, widths, heights map[int]float32) {
  421. if col == -1 || row == -1 {
  422. marker.Hide()
  423. marker.Refresh()
  424. return
  425. }
  426. xPos := offX
  427. for i := minCol; i < col; i++ {
  428. if width, ok := widths[i]; ok {
  429. xPos += width
  430. } else {
  431. xPos += t.cellSize.Width
  432. }
  433. xPos += theme.Padding()
  434. }
  435. x1 := xPos - t.scroll.Offset.X
  436. x2 := x1 + widths[col]
  437. yPos := offY
  438. for i := minRow; i < row; i++ {
  439. if height, ok := heights[i]; ok {
  440. yPos += height
  441. } else {
  442. yPos += t.cellSize.Height
  443. }
  444. yPos += theme.Padding()
  445. }
  446. y1 := yPos - t.scroll.Offset.Y
  447. y2 := y1 + heights[row]
  448. if x2 < 0 || x1 > t.t.size.Width || y2 < 0 || y1 > t.t.size.Height {
  449. marker.Hide()
  450. } else {
  451. left := fyne.Max(0, x1)
  452. top := fyne.Max(0, y1)
  453. marker.Move(fyne.NewPos(left, top))
  454. marker.Resize(fyne.NewSize(fyne.Min(x2, t.t.size.Width)-left, fyne.Min(y2, t.t.size.Height)-top))
  455. marker.Show()
  456. }
  457. marker.Refresh()
  458. }
  459. // Declare conformity with Hoverable interface.
  460. var _ desktop.Hoverable = (*tableCells)(nil)
  461. // Declare conformity with Tappable interface.
  462. var _ fyne.Tappable = (*tableCells)(nil)
  463. // Declare conformity with Widget interface.
  464. var _ fyne.Widget = (*tableCells)(nil)
  465. type tableCells struct {
  466. BaseWidget
  467. t *Table
  468. cellSize fyne.Size
  469. }
  470. func newTableCells(t *Table, s fyne.Size) *tableCells {
  471. c := &tableCells{t: t, cellSize: s}
  472. c.ExtendBaseWidget(c)
  473. return c
  474. }
  475. func (c *tableCells) CreateRenderer() fyne.WidgetRenderer {
  476. return &tableCellsRenderer{cells: c, pool: &syncPool{}, visible: make(map[TableCellID]fyne.CanvasObject)}
  477. }
  478. func (c *tableCells) MouseIn(ev *desktop.MouseEvent) {
  479. c.hoverAt(ev.Position)
  480. }
  481. func (c *tableCells) MouseMoved(ev *desktop.MouseEvent) {
  482. c.hoverAt(ev.Position)
  483. }
  484. func (c *tableCells) MouseOut() {
  485. c.hoverOut()
  486. }
  487. func (c *tableCells) Resize(s fyne.Size) {
  488. c.BaseWidget.Resize(s)
  489. c.Refresh() // trigger a redraw
  490. }
  491. func (c *tableCells) Tapped(e *fyne.PointEvent) {
  492. if e.Position.X < 0 || e.Position.X >= c.Size().Width || e.Position.Y < 0 || e.Position.Y >= c.Size().Height {
  493. c.t.selectedCell = nil
  494. c.t.Refresh()
  495. return
  496. }
  497. col := c.columnAt(e.Position)
  498. if col == -1 {
  499. return // out of col range
  500. }
  501. row := c.rowAt(e.Position)
  502. if row == -1 {
  503. return // out of row range
  504. }
  505. c.t.Select(TableCellID{row, col})
  506. }
  507. func (c *tableCells) columnAt(pos fyne.Position) int {
  508. dataCols := 0
  509. if f := c.t.Length; f != nil {
  510. _, dataCols = c.t.Length()
  511. }
  512. col := -1
  513. visibleColWidths, offX, minCol, _ := c.t.visibleColumnWidths(c.cellSize.Width, dataCols)
  514. i := minCol
  515. for x := offX; i < minCol+len(visibleColWidths); x += visibleColWidths[i-1] + theme.Padding() {
  516. if pos.X >= x && pos.X < x+visibleColWidths[i] {
  517. col = i
  518. }
  519. i++
  520. }
  521. return col
  522. }
  523. func (c *tableCells) hoverAt(pos fyne.Position) {
  524. if pos.X < 0 || pos.X >= c.Size().Width || pos.Y < 0 || pos.Y >= c.Size().Height {
  525. c.hoverOut()
  526. return
  527. }
  528. col := c.columnAt(pos)
  529. row := c.rowAt(pos)
  530. c.t.hoveredCell = &TableCellID{row, col}
  531. rows, cols := 0, 0
  532. if f := c.t.Length; f != nil {
  533. rows, cols = c.t.Length()
  534. }
  535. if c.t.hoveredCell.Col >= cols || c.t.hoveredCell.Row >= rows || c.t.hoveredCell.Col < 0 || c.t.hoveredCell.Row < 0 {
  536. c.hoverOut()
  537. return
  538. }
  539. if c.t.moveCallback != nil {
  540. c.t.moveCallback()
  541. }
  542. }
  543. func (c *tableCells) hoverOut() {
  544. c.t.hoveredCell = nil
  545. if c.t.moveCallback != nil {
  546. c.t.moveCallback()
  547. }
  548. }
  549. func (c *tableCells) rowAt(pos fyne.Position) int {
  550. dataRows := 0
  551. if f := c.t.Length; f != nil {
  552. dataRows, _ = c.t.Length()
  553. }
  554. row := -1
  555. visibleRowHeights, offY, minRow, _ := c.t.visibleRowHeights(c.cellSize.Height, dataRows)
  556. i := minRow
  557. for y := offY; i < minRow+len(visibleRowHeights); y += visibleRowHeights[i-1] + theme.Padding() {
  558. if pos.Y >= y && pos.Y < y+visibleRowHeights[i] {
  559. row = i
  560. }
  561. i++
  562. }
  563. return row
  564. }
  565. // Declare conformity with WidgetRenderer interface.
  566. var _ fyne.WidgetRenderer = (*tableCellsRenderer)(nil)
  567. type tableCellsRenderer struct {
  568. widget.BaseRenderer
  569. cells *tableCells
  570. pool pool
  571. visible map[TableCellID]fyne.CanvasObject
  572. }
  573. func (r *tableCellsRenderer) Layout(_ fyne.Size) {
  574. // we deal with cached objects so just refresh instead
  575. }
  576. func (r *tableCellsRenderer) MinSize() fyne.Size {
  577. rows, cols := 0, 0
  578. if f := r.cells.t.Length; f != nil {
  579. rows, cols = r.cells.t.Length()
  580. } else {
  581. fyne.LogError("Missing Length callback required for Table", nil)
  582. }
  583. width := float32(0)
  584. if len(r.cells.t.columnWidths) == 0 {
  585. width = r.cells.cellSize.Width * float32(cols)
  586. } else {
  587. cellWidth := r.cells.cellSize.Width
  588. for col := 0; col < cols; col++ {
  589. colWidth, ok := r.cells.t.columnWidths[col]
  590. if ok {
  591. width += colWidth
  592. } else {
  593. width += cellWidth
  594. }
  595. }
  596. }
  597. height := float32(0)
  598. if len(r.cells.t.rowHeights) == 0 {
  599. height = r.cells.cellSize.Height * float32(rows)
  600. } else {
  601. cellHeight := r.cells.cellSize.Height
  602. for row := 0; row < rows; row++ {
  603. rowHeight, ok := r.cells.t.rowHeights[row]
  604. if ok {
  605. height += rowHeight
  606. } else {
  607. height += cellHeight
  608. }
  609. }
  610. }
  611. separatorSize := theme.Padding()
  612. return fyne.NewSize(width+float32(cols-1)*separatorSize, height+float32(rows-1)*separatorSize)
  613. }
  614. func (r *tableCellsRenderer) Refresh() {
  615. r.cells.propertyLock.Lock()
  616. oldSize := r.cells.cellSize
  617. r.cells.cellSize = r.cells.t.templateSize()
  618. if oldSize != r.cells.cellSize { // theme changed probably
  619. r.returnAllToPool()
  620. }
  621. separatorThickness := theme.Padding()
  622. dataRows, dataCols := 0, 0
  623. if f := r.cells.t.Length; f != nil {
  624. dataRows, dataCols = r.cells.t.Length()
  625. }
  626. visibleColWidths, offX, minCol, maxCol := r.cells.t.visibleColumnWidths(r.cells.cellSize.Width, dataCols)
  627. if len(visibleColWidths) == 0 { // we can't show anything until we have some dimensions
  628. r.cells.propertyLock.Unlock()
  629. return
  630. }
  631. visibleRowHeights, offY, minRow, maxRow := r.cells.t.visibleRowHeights(r.cells.cellSize.Height, dataRows)
  632. if len(visibleRowHeights) == 0 { // we can't show anything until we have some dimensions
  633. r.cells.propertyLock.Unlock()
  634. return
  635. }
  636. updateCell := r.cells.t.UpdateCell
  637. if updateCell == nil {
  638. fyne.LogError("Missing UpdateCell callback required for Table", nil)
  639. }
  640. wasVisible := r.visible
  641. r.visible = make(map[TableCellID]fyne.CanvasObject)
  642. var cells []fyne.CanvasObject
  643. cellYOffset := offY
  644. for row := minRow; row < maxRow; row++ {
  645. rowHeight := visibleRowHeights[row]
  646. cellXOffset := offX
  647. for col := minCol; col < maxCol; col++ {
  648. id := TableCellID{row, col}
  649. colWidth := visibleColWidths[col]
  650. c, ok := wasVisible[id]
  651. if !ok {
  652. c = r.pool.Obtain()
  653. if f := r.cells.t.CreateCell; f != nil && c == nil {
  654. c = f()
  655. }
  656. if c == nil {
  657. continue
  658. }
  659. }
  660. c.Move(fyne.NewPos(cellXOffset, cellYOffset))
  661. c.Resize(fyne.NewSize(colWidth, rowHeight))
  662. r.visible[id] = c
  663. cells = append(cells, c)
  664. cellXOffset += colWidth + separatorThickness
  665. }
  666. cellYOffset += rowHeight + separatorThickness
  667. }
  668. for id, old := range wasVisible {
  669. if _, ok := r.visible[id]; !ok {
  670. r.pool.Release(old)
  671. }
  672. }
  673. visible := r.visible
  674. r.cells.propertyLock.Unlock()
  675. r.SetObjects(cells)
  676. if updateCell != nil {
  677. for id, cell := range visible {
  678. updateCell(TableCellID{id.Row, id.Col}, cell)
  679. }
  680. }
  681. }
  682. func (r *tableCellsRenderer) returnAllToPool() {
  683. for _, cell := range r.BaseRenderer.Objects() {
  684. r.pool.Release(cell)
  685. }
  686. r.visible = make(map[TableCellID]fyne.CanvasObject)
  687. r.SetObjects(nil)
  688. }