table.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  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. t.scroll.Offset.Y = y
  191. t.offset.Y = y
  192. t.finishScroll()
  193. }
  194. // ScrollToLeading scrolls horizontally to the leading edge of the table
  195. //
  196. // Since: 2.1
  197. func (t *Table) ScrollToLeading() {
  198. if t.scroll == nil {
  199. return
  200. }
  201. t.scroll.Offset.X = 0
  202. t.offset.X = 0
  203. t.finishScroll()
  204. }
  205. // ScrollToTop scrolls to the first row in the table
  206. //
  207. // Since: 2.1
  208. func (t *Table) ScrollToTop() {
  209. if t.scroll == nil {
  210. return
  211. }
  212. t.scroll.Offset.Y = 0
  213. t.offset.Y = 0
  214. t.finishScroll()
  215. }
  216. // ScrollToTrailing scrolls horizontally to the trailing edge of the table
  217. //
  218. // Since: 2.1
  219. func (t *Table) ScrollToTrailing() {
  220. if t.scroll == nil || t.Length == nil {
  221. return
  222. }
  223. _, cols := t.Length()
  224. cellX, cellWidth := t.findX(cols - 1)
  225. scrollX := cellX + cellWidth - t.scroll.Size().Width
  226. t.scroll.Offset.X = scrollX
  227. t.offset.X = scrollX
  228. t.finishScroll()
  229. }
  230. func (t *Table) findX(col int) (cellX float32, cellWidth float32) {
  231. cellSize := t.templateSize()
  232. for i := 0; i <= col; i++ {
  233. if cellWidth > 0 {
  234. cellX += cellWidth + theme.Padding()
  235. }
  236. width := cellSize.Width
  237. if w, ok := t.columnWidths[i]; ok {
  238. width = w
  239. }
  240. cellWidth = width
  241. }
  242. return
  243. }
  244. func (t *Table) findY(row int) (cellY float32, cellHeight float32) {
  245. cellSize := t.templateSize()
  246. for i := 0; i <= row; i++ {
  247. if cellHeight > 0 {
  248. cellY += cellHeight + theme.Padding()
  249. }
  250. height := cellSize.Height
  251. if h, ok := t.rowHeights[i]; ok {
  252. height = h
  253. }
  254. cellHeight = height
  255. }
  256. return
  257. }
  258. func (t *Table) finishScroll() {
  259. if t.moveCallback != nil {
  260. t.moveCallback()
  261. }
  262. t.scroll.Refresh()
  263. t.cells.Refresh()
  264. }
  265. func (t *Table) templateSize() fyne.Size {
  266. if f := t.CreateCell; f != nil {
  267. template := f() // don't use cache, we need new template
  268. return template.MinSize()
  269. }
  270. fyne.LogError("Missing CreateCell callback required for Table", nil)
  271. return fyne.Size{}
  272. }
  273. func (t *Table) visibleColumnWidths(colWidth float32, cols int) (visible map[int]float32, offX float32, minCol, maxCol int) {
  274. maxCol = cols
  275. colOffset := float32(0)
  276. isVisible := false
  277. visible = make(map[int]float32)
  278. if t.scroll.Size().Width <= 0 {
  279. return
  280. }
  281. for i := 0; i < cols; i++ {
  282. width := colWidth
  283. if w, ok := t.columnWidths[i]; ok {
  284. width = w
  285. }
  286. if colOffset <= t.offset.X-width-theme.Padding() {
  287. // before scroll
  288. } else if colOffset <= t.offset.X {
  289. minCol = i
  290. offX = colOffset
  291. isVisible = true
  292. }
  293. if colOffset < t.offset.X+t.scroll.Size().Width {
  294. maxCol = i + 1
  295. } else {
  296. break
  297. }
  298. colOffset += width + theme.Padding()
  299. if isVisible {
  300. visible[i] = width
  301. }
  302. }
  303. return
  304. }
  305. func (t *Table) visibleRowHeights(rowHeight float32, rows int) (visible map[int]float32, offY float32, minRow, maxRow int) {
  306. maxRow = rows
  307. rowOffset := float32(0)
  308. isVisible := false
  309. visible = make(map[int]float32)
  310. if t.scroll.Size().Height <= 0 {
  311. return
  312. }
  313. for i := 0; i < rows; i++ {
  314. height := rowHeight
  315. if h, ok := t.rowHeights[i]; ok {
  316. height = h
  317. }
  318. if rowOffset <= t.offset.Y-height-theme.Padding() {
  319. // before scroll
  320. } else if rowOffset <= t.offset.Y {
  321. minRow = i
  322. offY = rowOffset
  323. isVisible = true
  324. }
  325. if rowOffset < t.offset.Y+t.scroll.Size().Height {
  326. maxRow = i + 1
  327. } else {
  328. break
  329. }
  330. rowOffset += height + theme.Padding()
  331. if isVisible {
  332. visible[i] = height
  333. }
  334. }
  335. return
  336. }
  337. // Declare conformity with WidgetRenderer interface.
  338. var _ fyne.WidgetRenderer = (*tableRenderer)(nil)
  339. type tableRenderer struct {
  340. widget.BaseRenderer
  341. t *Table
  342. scroll *widget.Scroll
  343. hover, marker *canvas.Rectangle
  344. dividers []fyne.CanvasObject
  345. cellSize fyne.Size
  346. }
  347. func (t *tableRenderer) Layout(s fyne.Size) {
  348. t.scroll.Resize(s)
  349. t.moveIndicators()
  350. }
  351. func (t *tableRenderer) MinSize() fyne.Size {
  352. return t.t.scroll.MinSize().Max(t.cellSize)
  353. }
  354. func (t *tableRenderer) Refresh() {
  355. t.cellSize = t.t.templateSize()
  356. t.moveIndicators()
  357. t.marker.FillColor = theme.SelectionColor()
  358. t.marker.Refresh()
  359. t.hover.FillColor = theme.HoverColor()
  360. t.hover.Refresh()
  361. t.t.cells.Refresh()
  362. }
  363. func (t *tableRenderer) moveIndicators() {
  364. rows, cols := 0, 0
  365. if f := t.t.Length; f != nil {
  366. rows, cols = t.t.Length()
  367. }
  368. visibleColWidths, offX, minCol, maxCol := t.t.visibleColumnWidths(t.cellSize.Width, cols)
  369. visibleRowHeights, offY, minRow, maxRow := t.t.visibleRowHeights(t.cellSize.Height, rows)
  370. separatorThickness := theme.SeparatorThicknessSize()
  371. dividerOff := (theme.Padding() - separatorThickness) / 2
  372. if t.t.selectedCell == nil {
  373. t.moveMarker(t.marker, -1, -1, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  374. } else {
  375. t.moveMarker(t.marker, t.t.selectedCell.Row, t.t.selectedCell.Col, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  376. }
  377. if t.t.hoveredCell == nil {
  378. t.moveMarker(t.hover, -1, -1, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  379. } else {
  380. t.moveMarker(t.hover, t.t.hoveredCell.Row, t.t.hoveredCell.Col, offX, offY, minCol, minRow, visibleColWidths, visibleRowHeights)
  381. }
  382. colDivs := maxCol - minCol - 1
  383. rowDivs := maxRow - minRow - 1
  384. if len(t.dividers) < colDivs+rowDivs {
  385. for i := len(t.dividers); i < colDivs+rowDivs; i++ {
  386. t.dividers = append(t.dividers, NewSeparator())
  387. }
  388. obj := []fyne.CanvasObject{t.marker, t.hover}
  389. obj = append(obj, t.dividers...)
  390. t.SetObjects(append(obj, t.scroll))
  391. }
  392. divs := 0
  393. i := minCol
  394. for x := offX + visibleColWidths[i]; i < minCol+colDivs && divs < len(t.dividers); x += visibleColWidths[i] + theme.Padding() {
  395. i++
  396. t.dividers[divs].Move(fyne.NewPos(x-t.scroll.Offset.X+dividerOff, 0))
  397. t.dividers[divs].Resize(fyne.NewSize(separatorThickness, t.t.size.Height))
  398. t.dividers[divs].Show()
  399. divs++
  400. }
  401. i = minRow
  402. for y := offY + visibleRowHeights[i]; i < minRow+rowDivs && divs < len(t.dividers); y += visibleRowHeights[i] + theme.Padding() {
  403. i++
  404. t.dividers[divs].Move(fyne.NewPos(0, y-t.scroll.Offset.Y+dividerOff))
  405. t.dividers[divs].Resize(fyne.NewSize(t.t.size.Width, separatorThickness))
  406. t.dividers[divs].Show()
  407. divs++
  408. }
  409. for i := divs; i < len(t.dividers); i++ {
  410. t.dividers[i].Hide()
  411. }
  412. canvas.Refresh(t.t)
  413. }
  414. func (t *tableRenderer) moveMarker(marker fyne.CanvasObject, row, col int, offX, offY float32, minCol, minRow int, widths, heights map[int]float32) {
  415. if col == -1 || row == -1 {
  416. marker.Hide()
  417. marker.Refresh()
  418. return
  419. }
  420. xPos := offX
  421. for i := minCol; i < col; i++ {
  422. if width, ok := widths[i]; ok {
  423. xPos += width
  424. } else {
  425. xPos += t.cellSize.Width
  426. }
  427. xPos += theme.Padding()
  428. }
  429. x1 := xPos - t.scroll.Offset.X
  430. x2 := x1 + widths[col]
  431. yPos := offY
  432. for i := minRow; i < row; i++ {
  433. if height, ok := heights[i]; ok {
  434. yPos += height
  435. } else {
  436. yPos += t.cellSize.Height
  437. }
  438. yPos += theme.Padding()
  439. }
  440. y1 := yPos - t.scroll.Offset.Y
  441. y2 := y1 + heights[row]
  442. if x2 < 0 || x1 > t.t.size.Width || y2 < 0 || y1 > t.t.size.Height {
  443. marker.Hide()
  444. } else {
  445. left := fyne.Max(0, x1)
  446. top := fyne.Max(0, y1)
  447. marker.Move(fyne.NewPos(left, top))
  448. marker.Resize(fyne.NewSize(fyne.Min(x2, t.t.size.Width)-left, fyne.Min(y2, t.t.size.Height)-top))
  449. marker.Show()
  450. }
  451. marker.Refresh()
  452. }
  453. // Declare conformity with Hoverable interface.
  454. var _ desktop.Hoverable = (*tableCells)(nil)
  455. // Declare conformity with Tappable interface.
  456. var _ fyne.Tappable = (*tableCells)(nil)
  457. // Declare conformity with Widget interface.
  458. var _ fyne.Widget = (*tableCells)(nil)
  459. type tableCells struct {
  460. BaseWidget
  461. t *Table
  462. cellSize fyne.Size
  463. }
  464. func newTableCells(t *Table, s fyne.Size) *tableCells {
  465. c := &tableCells{t: t, cellSize: s}
  466. c.ExtendBaseWidget(c)
  467. return c
  468. }
  469. func (c *tableCells) CreateRenderer() fyne.WidgetRenderer {
  470. return &tableCellsRenderer{cells: c, pool: &syncPool{}, visible: make(map[TableCellID]fyne.CanvasObject)}
  471. }
  472. func (c *tableCells) MouseIn(ev *desktop.MouseEvent) {
  473. c.hoverAt(ev.Position)
  474. }
  475. func (c *tableCells) MouseMoved(ev *desktop.MouseEvent) {
  476. c.hoverAt(ev.Position)
  477. }
  478. func (c *tableCells) MouseOut() {
  479. c.hoverOut()
  480. }
  481. func (c *tableCells) Resize(s fyne.Size) {
  482. c.BaseWidget.Resize(s)
  483. c.Refresh() // trigger a redraw
  484. }
  485. func (c *tableCells) Tapped(e *fyne.PointEvent) {
  486. if e.Position.X < 0 || e.Position.X >= c.Size().Width || e.Position.Y < 0 || e.Position.Y >= c.Size().Height {
  487. c.t.selectedCell = nil
  488. c.t.Refresh()
  489. return
  490. }
  491. col := c.columnAt(e.Position)
  492. if col == -1 {
  493. return // out of col range
  494. }
  495. row := c.rowAt(e.Position)
  496. if row == -1 {
  497. return // out of row range
  498. }
  499. c.t.Select(TableCellID{row, col})
  500. }
  501. func (c *tableCells) columnAt(pos fyne.Position) int {
  502. dataCols := 0
  503. if f := c.t.Length; f != nil {
  504. _, dataCols = c.t.Length()
  505. }
  506. col := -1
  507. visibleColWidths, offX, minCol, _ := c.t.visibleColumnWidths(c.cellSize.Width, dataCols)
  508. i := minCol
  509. for x := offX; i < minCol+len(visibleColWidths); x += visibleColWidths[i-1] + theme.Padding() {
  510. if pos.X >= x && pos.X < x+visibleColWidths[i] {
  511. col = i
  512. }
  513. i++
  514. }
  515. return col
  516. }
  517. func (c *tableCells) hoverAt(pos fyne.Position) {
  518. if pos.X < 0 || pos.X >= c.Size().Width || pos.Y < 0 || pos.Y >= c.Size().Height {
  519. c.hoverOut()
  520. return
  521. }
  522. col := c.columnAt(pos)
  523. row := c.rowAt(pos)
  524. c.t.hoveredCell = &TableCellID{row, col}
  525. rows, cols := 0, 0
  526. if f := c.t.Length; f != nil {
  527. rows, cols = c.t.Length()
  528. }
  529. if c.t.hoveredCell.Col >= cols || c.t.hoveredCell.Row >= rows || c.t.hoveredCell.Col < 0 || c.t.hoveredCell.Row < 0 {
  530. c.hoverOut()
  531. return
  532. }
  533. if c.t.moveCallback != nil {
  534. c.t.moveCallback()
  535. }
  536. }
  537. func (c *tableCells) hoverOut() {
  538. c.t.hoveredCell = nil
  539. if c.t.moveCallback != nil {
  540. c.t.moveCallback()
  541. }
  542. }
  543. func (c *tableCells) rowAt(pos fyne.Position) int {
  544. dataRows := 0
  545. if f := c.t.Length; f != nil {
  546. dataRows, _ = c.t.Length()
  547. }
  548. row := -1
  549. visibleRowHeights, offY, minRow, _ := c.t.visibleRowHeights(c.cellSize.Height, dataRows)
  550. i := minRow
  551. for y := offY; i < minRow+len(visibleRowHeights); y += visibleRowHeights[i-1] + theme.Padding() {
  552. if pos.Y >= y && pos.Y < y+visibleRowHeights[i] {
  553. row = i
  554. }
  555. i++
  556. }
  557. return row
  558. }
  559. // Declare conformity with WidgetRenderer interface.
  560. var _ fyne.WidgetRenderer = (*tableCellsRenderer)(nil)
  561. type tableCellsRenderer struct {
  562. widget.BaseRenderer
  563. cells *tableCells
  564. pool pool
  565. visible map[TableCellID]fyne.CanvasObject
  566. }
  567. func (r *tableCellsRenderer) Layout(_ fyne.Size) {
  568. // we deal with cached objects so just refresh instead
  569. }
  570. func (r *tableCellsRenderer) MinSize() fyne.Size {
  571. rows, cols := 0, 0
  572. if f := r.cells.t.Length; f != nil {
  573. rows, cols = r.cells.t.Length()
  574. } else {
  575. fyne.LogError("Missing Length callback required for Table", nil)
  576. }
  577. width := float32(0)
  578. if len(r.cells.t.columnWidths) == 0 {
  579. width = r.cells.cellSize.Width * float32(cols)
  580. } else {
  581. cellWidth := r.cells.cellSize.Width
  582. for col := 0; col < cols; col++ {
  583. colWidth, ok := r.cells.t.columnWidths[col]
  584. if ok {
  585. width += colWidth
  586. } else {
  587. width += cellWidth
  588. }
  589. }
  590. }
  591. height := float32(0)
  592. if len(r.cells.t.rowHeights) == 0 {
  593. height = r.cells.cellSize.Height * float32(rows)
  594. } else {
  595. cellHeight := r.cells.cellSize.Height
  596. for row := 0; row < rows; row++ {
  597. rowHeight, ok := r.cells.t.rowHeights[row]
  598. if ok {
  599. height += rowHeight
  600. } else {
  601. height += cellHeight
  602. }
  603. }
  604. }
  605. separatorSize := theme.Padding()
  606. return fyne.NewSize(width+float32(cols-1)*separatorSize, height+float32(rows-1)*separatorSize)
  607. }
  608. func (r *tableCellsRenderer) Refresh() {
  609. r.cells.propertyLock.Lock()
  610. oldSize := r.cells.cellSize
  611. r.cells.cellSize = r.cells.t.templateSize()
  612. if oldSize != r.cells.cellSize { // theme changed probably
  613. r.returnAllToPool()
  614. }
  615. separatorThickness := theme.Padding()
  616. dataRows, dataCols := 0, 0
  617. if f := r.cells.t.Length; f != nil {
  618. dataRows, dataCols = r.cells.t.Length()
  619. }
  620. visibleColWidths, offX, minCol, maxCol := r.cells.t.visibleColumnWidths(r.cells.cellSize.Width, dataCols)
  621. if len(visibleColWidths) == 0 { // we can't show anything until we have some dimensions
  622. r.cells.propertyLock.Unlock()
  623. return
  624. }
  625. visibleRowHeights, offY, minRow, maxRow := r.cells.t.visibleRowHeights(r.cells.cellSize.Height, dataRows)
  626. if len(visibleRowHeights) == 0 { // we can't show anything until we have some dimensions
  627. r.cells.propertyLock.Unlock()
  628. return
  629. }
  630. updateCell := r.cells.t.UpdateCell
  631. if updateCell == nil {
  632. fyne.LogError("Missing UpdateCell callback required for Table", nil)
  633. }
  634. wasVisible := r.visible
  635. r.visible = make(map[TableCellID]fyne.CanvasObject)
  636. var cells []fyne.CanvasObject
  637. cellYOffset := offY
  638. for row := minRow; row < maxRow; row++ {
  639. rowHeight := visibleRowHeights[row]
  640. cellXOffset := offX
  641. for col := minCol; col < maxCol; col++ {
  642. id := TableCellID{row, col}
  643. colWidth := visibleColWidths[col]
  644. c, ok := wasVisible[id]
  645. if !ok {
  646. c = r.pool.Obtain()
  647. if f := r.cells.t.CreateCell; f != nil && c == nil {
  648. c = f()
  649. }
  650. if c == nil {
  651. continue
  652. }
  653. }
  654. c.Move(fyne.NewPos(cellXOffset, cellYOffset))
  655. c.Resize(fyne.NewSize(colWidth, rowHeight))
  656. r.visible[id] = c
  657. cells = append(cells, c)
  658. cellXOffset += colWidth + separatorThickness
  659. }
  660. cellYOffset += rowHeight + separatorThickness
  661. }
  662. for id, old := range wasVisible {
  663. if _, ok := r.visible[id]; !ok {
  664. r.pool.Release(old)
  665. }
  666. }
  667. visible := r.visible
  668. r.cells.propertyLock.Unlock()
  669. r.SetObjects(cells)
  670. if updateCell != nil {
  671. for id, cell := range visible {
  672. updateCell(TableCellID{id.Row, id.Col}, cell)
  673. }
  674. }
  675. }
  676. func (r *tableCellsRenderer) returnAllToPool() {
  677. for _, cell := range r.BaseRenderer.Objects() {
  678. r.pool.Release(cell)
  679. }
  680. r.visible = make(map[TableCellID]fyne.CanvasObject)
  681. r.SetObjects(nil)
  682. }