grid.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. package tview
  2. import (
  3. "math"
  4. "github.com/gdamore/tcell"
  5. )
  6. // gridItem represents one primitive and its possible position on a grid.
  7. type gridItem struct {
  8. Item Primitive // The item to be positioned. May be nil for an empty item.
  9. Row, Column int // The top-left grid cell where the item is placed.
  10. Width, Height int // The number of rows and columns the item occupies.
  11. MinGridWidth, MinGridHeight int // The minimum grid width/height for which this item is visible.
  12. Focus bool // Whether or not this item attracts the layout's focus.
  13. visible bool // Whether or not this item was visible the last time the grid was drawn.
  14. x, y, w, h int // The last position of the item relative to the top-left corner of the grid. Undefined if visible is false.
  15. }
  16. // Grid is an implementation of a grid-based layout. It works by defining the
  17. // size of the rows and columns, then placing primitives into the grid.
  18. //
  19. // Some settings can lead to the grid exceeding its available space. SetOffset()
  20. // can then be used to scroll in steps of rows and columns. These offset values
  21. // can also be controlled with the arrow keys (or the "g","G", "j", "k", "h",
  22. // and "l" keys) while the grid has focus and none of its contained primitives
  23. // do.
  24. //
  25. // See https://github.com/rivo/tview/wiki/Grid for an example.
  26. type Grid struct {
  27. *Box
  28. // The items to be positioned.
  29. items []*gridItem
  30. // The definition of the rows and columns of the grid. See
  31. // SetRows()/SetColumns() for details.
  32. rows, columns []int
  33. // The minimum sizes for rows and columns.
  34. minWidth, minHeight int
  35. // The size of the gaps between neighboring primitives. This is automatically
  36. // set to 1 if borders is true.
  37. gapRows, gapColumns int
  38. // The number of rows and columns skipped before drawing the top-left corner
  39. // of the grid.
  40. rowOffset, columnOffset int
  41. // Whether or not borders are drawn around grid items. If this is set to true,
  42. // a gap size of 1 is automatically assumed (which is filled with the border
  43. // graphics).
  44. borders bool
  45. // The color of the borders around grid items.
  46. bordersColor tcell.Color
  47. }
  48. // NewGrid returns a new grid-based layout container with no initial primitives.
  49. //
  50. // Note that Box, the superclass of Grid, will have its background color set to
  51. // transparent so that any grid areas not covered by any primitives will leave
  52. // their background unchanged. To clear a Grid's background before any items are
  53. // drawn, set it to the desired color:
  54. //
  55. // grid.SetBackgroundColor(tview.Styles.PrimitiveBackgroundColor)
  56. func NewGrid() *Grid {
  57. g := &Grid{
  58. Box: NewBox().SetBackgroundColor(tcell.ColorDefault),
  59. bordersColor: Styles.GraphicsColor,
  60. }
  61. g.focus = g
  62. return g
  63. }
  64. // SetColumns defines how the columns of the grid are distributed. Each value
  65. // defines the size of one column, starting with the leftmost column. Values
  66. // greater 0 represent absolute column widths (gaps not included). Values less
  67. // or equal 0 represent proportional column widths or fractions of the remaining
  68. // free space, where 0 is treated the same as -1. That is, a column with a value
  69. // of -3 will have three times the width of a column with a value of -1 (or 0).
  70. // The minimum width set with SetMinSize() is always observed.
  71. //
  72. // Primitives may extend beyond the columns defined explicitly with this
  73. // function. A value of 0 is assumed for any undefined column. In fact, if you
  74. // never call this function, all columns occupied by primitives will have the
  75. // same width. On the other hand, unoccupied columns defined with this function
  76. // will always take their place.
  77. //
  78. // Assuming a total width of the grid of 100 cells and a minimum width of 0, the
  79. // following call will result in columns with widths of 30, 10, 15, 15, and 30
  80. // cells:
  81. //
  82. // grid.Setcolumns(30, 10, -1, -1, -2)
  83. //
  84. // If a primitive were then placed in the 6th and 7th column, the resulting
  85. // widths would be: 30, 10, 10, 10, 20, 10, and 10 cells.
  86. //
  87. // If you then called SetMinSize() as follows:
  88. //
  89. // grid.SetMinSize(15, 20)
  90. //
  91. // The resulting widths would be: 30, 15, 15, 15, 20, 15, and 15 cells, a total
  92. // of 125 cells, 25 cells wider than the available grid width.
  93. func (g *Grid) SetColumns(columns ...int) *Grid {
  94. g.columns = columns
  95. return g
  96. }
  97. // SetRows defines how the rows of the grid are distributed. These values behave
  98. // the same as the column values provided with SetColumns(), see there for a
  99. // definition and examples.
  100. //
  101. // The provided values correspond to row heights, the first value defining
  102. // the height of the topmost row.
  103. func (g *Grid) SetRows(rows ...int) *Grid {
  104. g.rows = rows
  105. return g
  106. }
  107. // SetSize is a shortcut for SetRows() and SetColumns() where all row and column
  108. // values are set to the given size values. See SetColumns() for details on sizes.
  109. func (g *Grid) SetSize(numRows, numColumns, rowSize, columnSize int) *Grid {
  110. g.rows = make([]int, numRows)
  111. for index := range g.rows {
  112. g.rows[index] = rowSize
  113. }
  114. g.columns = make([]int, numColumns)
  115. for index := range g.columns {
  116. g.columns[index] = columnSize
  117. }
  118. return g
  119. }
  120. // SetMinSize sets an absolute minimum width for rows and an absolute minimum
  121. // height for columns. Panics if negative values are provided.
  122. func (g *Grid) SetMinSize(row, column int) *Grid {
  123. if row < 0 || column < 0 {
  124. panic("Invalid minimum row/column size")
  125. }
  126. g.minHeight, g.minWidth = row, column
  127. return g
  128. }
  129. // SetGap sets the size of the gaps between neighboring primitives on the grid.
  130. // If borders are drawn (see SetBorders()), these values are ignored and a gap
  131. // of 1 is assumed. Panics if negative values are provided.
  132. func (g *Grid) SetGap(row, column int) *Grid {
  133. if row < 0 || column < 0 {
  134. panic("Invalid gap size")
  135. }
  136. g.gapRows, g.gapColumns = row, column
  137. return g
  138. }
  139. // SetBorders sets whether or not borders are drawn around grid items. Setting
  140. // this value to true will cause the gap values (see SetGap()) to be ignored and
  141. // automatically assumed to be 1 where the border graphics are drawn.
  142. func (g *Grid) SetBorders(borders bool) *Grid {
  143. g.borders = borders
  144. return g
  145. }
  146. // SetBordersColor sets the color of the item borders.
  147. func (g *Grid) SetBordersColor(color tcell.Color) *Grid {
  148. g.bordersColor = color
  149. return g
  150. }
  151. // AddItem adds a primitive and its position to the grid. The top-left corner
  152. // of the primitive will be located in the top-left corner of the grid cell at
  153. // the given row and column and will span "rowSpan" rows and "colSpan" columns.
  154. // For example, for a primitive to occupy rows 2, 3, and 4 and columns 5 and 6:
  155. //
  156. // grid.AddItem(p, 2, 5, 3, 2, 0, 0, true)
  157. //
  158. // If rowSpan or colSpan is 0, the primitive will not be drawn.
  159. //
  160. // You can add the same primitive multiple times with different grid positions.
  161. // The minGridWidth and minGridHeight values will then determine which of those
  162. // positions will be used. This is similar to CSS media queries. These minimum
  163. // values refer to the overall size of the grid. If multiple items for the same
  164. // primitive apply, the one that has at least one highest minimum value will be
  165. // used, or the primitive added last if those values are the same. Example:
  166. //
  167. // grid.AddItem(p, 0, 0, 0, 0, 0, 0, true). // Hide in small grids.
  168. // AddItem(p, 0, 0, 1, 2, 100, 0, true). // One-column layout for medium grids.
  169. // AddItem(p, 1, 1, 3, 2, 300, 0, true) // Multi-column layout for large grids.
  170. //
  171. // To use the same grid layout for all sizes, simply set minGridWidth and
  172. // minGridHeight to 0.
  173. //
  174. // If the item's focus is set to true, it will receive focus when the grid
  175. // receives focus. If there are multiple items with a true focus flag, the last
  176. // visible one that was added will receive focus.
  177. func (g *Grid) AddItem(p Primitive, row, column, rowSpan, colSpan, minGridHeight, minGridWidth int, focus bool) *Grid {
  178. g.items = append(g.items, &gridItem{
  179. Item: p,
  180. Row: row,
  181. Column: column,
  182. Height: rowSpan,
  183. Width: colSpan,
  184. MinGridHeight: minGridHeight,
  185. MinGridWidth: minGridWidth,
  186. Focus: focus,
  187. })
  188. return g
  189. }
  190. // RemoveItem removes all items for the given primitive from the grid, keeping
  191. // the order of the remaining items intact.
  192. func (g *Grid) RemoveItem(p Primitive) *Grid {
  193. for index := len(g.items) - 1; index >= 0; index-- {
  194. if g.items[index].Item == p {
  195. g.items = append(g.items[:index], g.items[index+1:]...)
  196. }
  197. }
  198. return g
  199. }
  200. // Clear removes all items from the grid.
  201. func (g *Grid) Clear() *Grid {
  202. g.items = nil
  203. return g
  204. }
  205. // SetOffset sets the number of rows and columns which are skipped before
  206. // drawing the first grid cell in the top-left corner. As the grid will never
  207. // completely move off the screen, these values may be adjusted the next time
  208. // the grid is drawn. The actual position of the grid may also be adjusted such
  209. // that contained primitives that have focus remain visible.
  210. func (g *Grid) SetOffset(rows, columns int) *Grid {
  211. g.rowOffset, g.columnOffset = rows, columns
  212. return g
  213. }
  214. // GetOffset returns the current row and column offset (see SetOffset() for
  215. // details).
  216. func (g *Grid) GetOffset() (rows, columns int) {
  217. return g.rowOffset, g.columnOffset
  218. }
  219. // Focus is called when this primitive receives focus.
  220. func (g *Grid) Focus(delegate func(p Primitive)) {
  221. for _, item := range g.items {
  222. if item.Focus {
  223. delegate(item.Item)
  224. return
  225. }
  226. }
  227. g.hasFocus = true
  228. }
  229. // Blur is called when this primitive loses focus.
  230. func (g *Grid) Blur() {
  231. g.hasFocus = false
  232. }
  233. // HasFocus returns whether or not this primitive has focus.
  234. func (g *Grid) HasFocus() bool {
  235. for _, item := range g.items {
  236. if item.visible && item.Item.GetFocusable().HasFocus() {
  237. return true
  238. }
  239. }
  240. return g.hasFocus
  241. }
  242. // InputHandler returns the handler for this primitive.
  243. func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
  244. return g.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
  245. switch event.Key() {
  246. case tcell.KeyRune:
  247. switch event.Rune() {
  248. case 'g':
  249. g.rowOffset, g.columnOffset = 0, 0
  250. case 'G':
  251. g.rowOffset = math.MaxInt32
  252. case 'j':
  253. g.rowOffset++
  254. case 'k':
  255. g.rowOffset--
  256. case 'h':
  257. g.columnOffset--
  258. case 'l':
  259. g.columnOffset++
  260. }
  261. case tcell.KeyHome:
  262. g.rowOffset, g.columnOffset = 0, 0
  263. case tcell.KeyEnd:
  264. g.rowOffset = math.MaxInt32
  265. case tcell.KeyUp:
  266. g.rowOffset--
  267. case tcell.KeyDown:
  268. g.rowOffset++
  269. case tcell.KeyLeft:
  270. g.columnOffset--
  271. case tcell.KeyRight:
  272. g.columnOffset++
  273. }
  274. })
  275. }
  276. // Draw draws this primitive onto the screen.
  277. func (g *Grid) Draw(screen tcell.Screen) {
  278. g.Box.Draw(screen)
  279. x, y, width, height := g.GetInnerRect()
  280. screenWidth, screenHeight := screen.Size()
  281. // Make a list of items which apply.
  282. items := make(map[Primitive]*gridItem)
  283. for _, item := range g.items {
  284. item.visible = false
  285. if item.Width <= 0 || item.Height <= 0 || width < item.MinGridWidth || height < item.MinGridHeight {
  286. continue
  287. }
  288. previousItem, ok := items[item.Item]
  289. if ok && item.MinGridWidth < previousItem.MinGridWidth && item.MinGridHeight < previousItem.MinGridHeight {
  290. continue
  291. }
  292. items[item.Item] = item
  293. }
  294. // How many rows and columns do we have?
  295. rows := len(g.rows)
  296. columns := len(g.columns)
  297. for _, item := range items {
  298. rowEnd := item.Row + item.Height
  299. if rowEnd > rows {
  300. rows = rowEnd
  301. }
  302. columnEnd := item.Column + item.Width
  303. if columnEnd > columns {
  304. columns = columnEnd
  305. }
  306. }
  307. if rows == 0 || columns == 0 {
  308. return // No content.
  309. }
  310. // Where are they located?
  311. rowPos := make([]int, rows)
  312. rowHeight := make([]int, rows)
  313. columnPos := make([]int, columns)
  314. columnWidth := make([]int, columns)
  315. // How much space do we distribute?
  316. remainingWidth := width
  317. remainingHeight := height
  318. proportionalWidth := 0
  319. proportionalHeight := 0
  320. for index, row := range g.rows {
  321. if row > 0 {
  322. if row < g.minHeight {
  323. row = g.minHeight
  324. }
  325. remainingHeight -= row
  326. rowHeight[index] = row
  327. } else if row == 0 {
  328. proportionalHeight++
  329. } else {
  330. proportionalHeight += -row
  331. }
  332. }
  333. for index, column := range g.columns {
  334. if column > 0 {
  335. if column < g.minWidth {
  336. column = g.minWidth
  337. }
  338. remainingWidth -= column
  339. columnWidth[index] = column
  340. } else if column == 0 {
  341. proportionalWidth++
  342. } else {
  343. proportionalWidth += -column
  344. }
  345. }
  346. if g.borders {
  347. remainingHeight -= rows + 1
  348. remainingWidth -= columns + 1
  349. } else {
  350. remainingHeight -= (rows - 1) * g.gapRows
  351. remainingWidth -= (columns - 1) * g.gapColumns
  352. }
  353. if rows > len(g.rows) {
  354. proportionalHeight += rows - len(g.rows)
  355. }
  356. if columns > len(g.columns) {
  357. proportionalWidth += columns - len(g.columns)
  358. }
  359. // Distribute proportional rows/columns.
  360. for index := 0; index < rows; index++ {
  361. row := 0
  362. if index < len(g.rows) {
  363. row = g.rows[index]
  364. }
  365. if row > 0 {
  366. if row < g.minHeight {
  367. row = g.minHeight
  368. }
  369. continue // Not proportional. We already know the width.
  370. } else if row == 0 {
  371. row = 1
  372. } else {
  373. row = -row
  374. }
  375. rowAbs := row * remainingHeight / proportionalHeight
  376. remainingHeight -= rowAbs
  377. proportionalHeight -= row
  378. if rowAbs < g.minHeight {
  379. rowAbs = g.minHeight
  380. }
  381. rowHeight[index] = rowAbs
  382. }
  383. for index := 0; index < columns; index++ {
  384. column := 0
  385. if index < len(g.columns) {
  386. column = g.columns[index]
  387. }
  388. if column > 0 {
  389. if column < g.minWidth {
  390. column = g.minWidth
  391. }
  392. continue // Not proportional. We already know the height.
  393. } else if column == 0 {
  394. column = 1
  395. } else {
  396. column = -column
  397. }
  398. columnAbs := column * remainingWidth / proportionalWidth
  399. remainingWidth -= columnAbs
  400. proportionalWidth -= column
  401. if columnAbs < g.minWidth {
  402. columnAbs = g.minWidth
  403. }
  404. columnWidth[index] = columnAbs
  405. }
  406. // Calculate row/column positions.
  407. var columnX, rowY int
  408. if g.borders {
  409. columnX++
  410. rowY++
  411. }
  412. for index, row := range rowHeight {
  413. rowPos[index] = rowY
  414. gap := g.gapRows
  415. if g.borders {
  416. gap = 1
  417. }
  418. rowY += row + gap
  419. }
  420. for index, column := range columnWidth {
  421. columnPos[index] = columnX
  422. gap := g.gapColumns
  423. if g.borders {
  424. gap = 1
  425. }
  426. columnX += column + gap
  427. }
  428. // Calculate primitive positions.
  429. var focus *gridItem // The item which has focus.
  430. for primitive, item := range items {
  431. px := columnPos[item.Column]
  432. py := rowPos[item.Row]
  433. var pw, ph int
  434. for index := 0; index < item.Height; index++ {
  435. ph += rowHeight[item.Row+index]
  436. }
  437. for index := 0; index < item.Width; index++ {
  438. pw += columnWidth[item.Column+index]
  439. }
  440. if g.borders {
  441. pw += item.Width - 1
  442. ph += item.Height - 1
  443. } else {
  444. pw += (item.Width - 1) * g.gapColumns
  445. ph += (item.Height - 1) * g.gapRows
  446. }
  447. item.x, item.y, item.w, item.h = px, py, pw, ph
  448. item.visible = true
  449. if primitive.GetFocusable().HasFocus() {
  450. focus = item
  451. }
  452. }
  453. // Calculate screen offsets.
  454. var offsetX, offsetY int
  455. add := 1
  456. if !g.borders {
  457. add = g.gapRows
  458. }
  459. for index, height := range rowHeight {
  460. if index >= g.rowOffset {
  461. break
  462. }
  463. offsetY += height + add
  464. }
  465. if !g.borders {
  466. add = g.gapColumns
  467. }
  468. for index, width := range columnWidth {
  469. if index >= g.columnOffset {
  470. break
  471. }
  472. offsetX += width + add
  473. }
  474. // Line up the last row/column with the end of the available area.
  475. var border int
  476. if g.borders {
  477. border = 1
  478. }
  479. last := len(rowPos) - 1
  480. if rowPos[last]+rowHeight[last]+border-offsetY < height {
  481. offsetY = rowPos[last] - height + rowHeight[last] + border
  482. }
  483. last = len(columnPos) - 1
  484. if columnPos[last]+columnWidth[last]+border-offsetX < width {
  485. offsetX = columnPos[last] - width + columnWidth[last] + border
  486. }
  487. // The focused item must be within the visible area.
  488. if focus != nil {
  489. if focus.y+focus.h-offsetY >= height {
  490. offsetY = focus.y - height + focus.h
  491. }
  492. if focus.y-offsetY < 0 {
  493. offsetY = focus.y
  494. }
  495. if focus.x+focus.w-offsetX >= width {
  496. offsetX = focus.x - width + focus.w
  497. }
  498. if focus.x-offsetX < 0 {
  499. offsetX = focus.x
  500. }
  501. }
  502. // Adjust row/column offsets based on this value.
  503. var from, to int
  504. for index, pos := range rowPos {
  505. if pos-offsetY < 0 {
  506. from = index + 1
  507. }
  508. if pos-offsetY < height {
  509. to = index
  510. }
  511. }
  512. if g.rowOffset < from {
  513. g.rowOffset = from
  514. }
  515. if g.rowOffset > to {
  516. g.rowOffset = to
  517. }
  518. from, to = 0, 0
  519. for index, pos := range columnPos {
  520. if pos-offsetX < 0 {
  521. from = index + 1
  522. }
  523. if pos-offsetX < width {
  524. to = index
  525. }
  526. }
  527. if g.columnOffset < from {
  528. g.columnOffset = from
  529. }
  530. if g.columnOffset > to {
  531. g.columnOffset = to
  532. }
  533. // Draw primitives and borders.
  534. for primitive, item := range items {
  535. // Final primitive position.
  536. if !item.visible {
  537. continue
  538. }
  539. item.x -= offsetX
  540. item.y -= offsetY
  541. if item.x >= width || item.x+item.w <= 0 || item.y >= height || item.y+item.h <= 0 {
  542. item.visible = false
  543. continue
  544. }
  545. if item.x+item.w > width {
  546. item.w = width - item.x
  547. }
  548. if item.y+item.h > height {
  549. item.h = height - item.y
  550. }
  551. if item.x < 0 {
  552. item.w += item.x
  553. item.x = 0
  554. }
  555. if item.y < 0 {
  556. item.h += item.y
  557. item.y = 0
  558. }
  559. if item.w <= 0 || item.h <= 0 {
  560. item.visible = false
  561. continue
  562. }
  563. item.x += x
  564. item.y += y
  565. primitive.SetRect(item.x, item.y, item.w, item.h)
  566. // Draw primitive.
  567. if item == focus {
  568. defer primitive.Draw(screen)
  569. } else {
  570. primitive.Draw(screen)
  571. }
  572. // Draw border around primitive.
  573. if g.borders {
  574. for bx := item.x; bx < item.x+item.w; bx++ { // Top/bottom lines.
  575. if bx < 0 || bx >= screenWidth {
  576. continue
  577. }
  578. by := item.y - 1
  579. if by >= 0 && by < screenHeight {
  580. PrintJoinedSemigraphics(screen, bx, by, Borders.Horizontal, g.bordersColor)
  581. }
  582. by = item.y + item.h
  583. if by >= 0 && by < screenHeight {
  584. PrintJoinedSemigraphics(screen, bx, by, Borders.Horizontal, g.bordersColor)
  585. }
  586. }
  587. for by := item.y; by < item.y+item.h; by++ { // Left/right lines.
  588. if by < 0 || by >= screenHeight {
  589. continue
  590. }
  591. bx := item.x - 1
  592. if bx >= 0 && bx < screenWidth {
  593. PrintJoinedSemigraphics(screen, bx, by, Borders.Vertical, g.bordersColor)
  594. }
  595. bx = item.x + item.w
  596. if bx >= 0 && bx < screenWidth {
  597. PrintJoinedSemigraphics(screen, bx, by, Borders.Vertical, g.bordersColor)
  598. }
  599. }
  600. bx, by := item.x-1, item.y-1 // Top-left corner.
  601. if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight {
  602. PrintJoinedSemigraphics(screen, bx, by, Borders.TopLeft, g.bordersColor)
  603. }
  604. bx, by = item.x+item.w, item.y-1 // Top-right corner.
  605. if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight {
  606. PrintJoinedSemigraphics(screen, bx, by, Borders.TopRight, g.bordersColor)
  607. }
  608. bx, by = item.x-1, item.y+item.h // Bottom-left corner.
  609. if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight {
  610. PrintJoinedSemigraphics(screen, bx, by, Borders.BottomLeft, g.bordersColor)
  611. }
  612. bx, by = item.x+item.w, item.y+item.h // Bottom-right corner.
  613. if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight {
  614. PrintJoinedSemigraphics(screen, bx, by, Borders.BottomRight, g.bordersColor)
  615. }
  616. }
  617. }
  618. }
  619. // MouseHandler returns the mouse handler for this primitive.
  620. func (g *Grid) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
  621. return g.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
  622. if !g.InRect(event.Position()) {
  623. return false, nil
  624. }
  625. // Pass mouse events along to the first child item that takes it.
  626. for _, item := range g.items {
  627. if item.Item == nil {
  628. continue
  629. }
  630. consumed, capture = item.Item.MouseHandler()(action, event, setFocus)
  631. if consumed {
  632. return
  633. }
  634. }
  635. return
  636. })
  637. }