table.go 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653
  1. package tview
  2. import (
  3. "sort"
  4. "github.com/gdamore/tcell/v2"
  5. colorful "github.com/lucasb-eyer/go-colorful"
  6. )
  7. // TableCell represents one cell inside a Table. You can instantiate this type
  8. // directly but all colors (background and text) will be set to their default
  9. // which is black.
  10. type TableCell struct {
  11. // The reference object.
  12. Reference interface{}
  13. // The text to be displayed in the table cell.
  14. Text string
  15. // The alignment of the cell text. One of AlignLeft (default), AlignCenter,
  16. // or AlignRight.
  17. Align int
  18. // The maximum width of the cell in screen space. This is used to give a
  19. // column a maximum width. Any cell text whose screen width exceeds this width
  20. // is cut off. Set to 0 if there is no maximum width.
  21. MaxWidth int
  22. // If the total table width is less than the available width, this value is
  23. // used to add extra width to a column. See SetExpansion() for details.
  24. Expansion int
  25. // The color of the cell text.
  26. Color tcell.Color
  27. // The background color of the cell.
  28. BackgroundColor tcell.Color
  29. // If set to true, the BackgroundColor is not used and the cell will have
  30. // the background color of the table.
  31. Transparent bool
  32. // The style attributes of the cell.
  33. Attributes tcell.AttrMask
  34. // If set to true, this cell cannot be selected.
  35. NotSelectable bool
  36. // An optional handler for mouse clicks. This also fires if the cell is not
  37. // selectable. If true is returned, no additional "selected" event is fired
  38. // on selectable cells.
  39. Clicked func() bool
  40. // The position and width of the cell the last time table was drawn.
  41. x, y, width int
  42. }
  43. // NewTableCell returns a new table cell with sensible defaults. That is, left
  44. // aligned text with the primary text color (see Styles) and a transparent
  45. // background (using the background of the Table).
  46. func NewTableCell(text string) *TableCell {
  47. return &TableCell{
  48. Text: text,
  49. Align: AlignLeft,
  50. Color: Styles.PrimaryTextColor,
  51. BackgroundColor: Styles.PrimitiveBackgroundColor,
  52. Transparent: true,
  53. }
  54. }
  55. // SetText sets the cell's text.
  56. func (c *TableCell) SetText(text string) *TableCell {
  57. c.Text = text
  58. return c
  59. }
  60. // SetAlign sets the cell's text alignment, one of AlignLeft, AlignCenter, or
  61. // AlignRight.
  62. func (c *TableCell) SetAlign(align int) *TableCell {
  63. c.Align = align
  64. return c
  65. }
  66. // SetMaxWidth sets maximum width of the cell in screen space. This is used to
  67. // give a column a maximum width. Any cell text whose screen width exceeds this
  68. // width is cut off. Set to 0 if there is no maximum width.
  69. func (c *TableCell) SetMaxWidth(maxWidth int) *TableCell {
  70. c.MaxWidth = maxWidth
  71. return c
  72. }
  73. // SetExpansion sets the value by which the column of this cell expands if the
  74. // available width for the table is more than the table width (prior to applying
  75. // this expansion value). This is a proportional value. The amount of unused
  76. // horizontal space is divided into widths to be added to each column. How much
  77. // extra width a column receives depends on the expansion value: A value of 0
  78. // (the default) will not cause the column to increase in width. Other values
  79. // are proportional, e.g. a value of 2 will cause a column to grow by twice
  80. // the amount of a column with a value of 1.
  81. //
  82. // Since this value affects an entire column, the maximum over all visible cells
  83. // in that column is used.
  84. //
  85. // This function panics if a negative value is provided.
  86. func (c *TableCell) SetExpansion(expansion int) *TableCell {
  87. if expansion < 0 {
  88. panic("Table cell expansion values may not be negative")
  89. }
  90. c.Expansion = expansion
  91. return c
  92. }
  93. // SetTextColor sets the cell's text color.
  94. func (c *TableCell) SetTextColor(color tcell.Color) *TableCell {
  95. c.Color = color
  96. return c
  97. }
  98. // SetBackgroundColor sets the cell's background color. This will also cause the
  99. // cell's Transparent flag to be set to "false".
  100. func (c *TableCell) SetBackgroundColor(color tcell.Color) *TableCell {
  101. c.BackgroundColor = color
  102. c.Transparent = false
  103. return c
  104. }
  105. // SetTransparency sets the background transparency of this cell. A value of
  106. // "true" will cause the cell to use the table's background color. A value of
  107. // "false" will cause it to use its own background color.
  108. func (c *TableCell) SetTransparency(transparent bool) *TableCell {
  109. c.Transparent = transparent
  110. return c
  111. }
  112. // SetAttributes sets the cell's text attributes. You can combine different
  113. // attributes using bitmask operations:
  114. //
  115. // cell.SetAttributes(tcell.AttrUnderline | tcell.AttrBold)
  116. func (c *TableCell) SetAttributes(attr tcell.AttrMask) *TableCell {
  117. c.Attributes = attr
  118. return c
  119. }
  120. // SetStyle sets the cell's style (foreground color, background color, and
  121. // attributes) all at once.
  122. func (c *TableCell) SetStyle(style tcell.Style) *TableCell {
  123. c.Color, c.BackgroundColor, c.Attributes = style.Decompose()
  124. return c
  125. }
  126. // SetSelectable sets whether or not this cell can be selected by the user.
  127. func (c *TableCell) SetSelectable(selectable bool) *TableCell {
  128. c.NotSelectable = !selectable
  129. return c
  130. }
  131. // SetReference allows you to store a reference of any type in this cell. This
  132. // will allow you to establish a mapping between the cell and your
  133. // actual data.
  134. func (c *TableCell) SetReference(reference interface{}) *TableCell {
  135. c.Reference = reference
  136. return c
  137. }
  138. // GetReference returns this cell's reference object.
  139. func (c *TableCell) GetReference() interface{} {
  140. return c.Reference
  141. }
  142. // GetLastPosition returns the position of the table cell the last time it was
  143. // drawn on screen. If the cell is not on screen, the return values are
  144. // undefined.
  145. //
  146. // Because the Table class will attempt to keep selected cells on screen, this
  147. // function is most useful in response to a "selected" event (see
  148. // SetSelectedFunc()) or a "selectionChanged" event (see
  149. // SetSelectionChangedFunc()).
  150. func (c *TableCell) GetLastPosition() (x, y, width int) {
  151. return c.x, c.y, c.width
  152. }
  153. // SetClickedFunc sets a handler which fires when this cell is clicked. This is
  154. // independent of whether the cell is selectable or not. But for selectable
  155. // cells, if the function returns "true", the "selected" event is not fired.
  156. func (c *TableCell) SetClickedFunc(clicked func() bool) *TableCell {
  157. c.Clicked = clicked
  158. return c
  159. }
  160. // TableContent defines a Table's data. You may replace a Table's default
  161. // implementation with your own using the Table.SetContent() function. This will
  162. // allow you to turn Table into a view of your own data structure. The
  163. // Table.Draw() function, which is called when the screen is updated, will then
  164. // use the (read-only) functions of this interface to update the table. The
  165. // write functions are only called when the corresponding functions of Table are
  166. // called.
  167. //
  168. // The interface's read-only functions are not called concurrently by the
  169. // package (provided that users of the package don't call Table.Draw() in a
  170. // separate goroutine, which would be uncommon and is not encouraged).
  171. type TableContent interface {
  172. // Return the cell at the given position or nil if there is no cell. The
  173. // row and column arguments start at 0 and end at what GetRowCount() and
  174. // GetColumnCount() return, minus 1.
  175. GetCell(row, column int) *TableCell
  176. // Return the total number of rows in the table.
  177. GetRowCount() int
  178. // Return the total number of columns in the table.
  179. GetColumnCount() int
  180. // The following functions are provided for completeness reasons as the
  181. // original Table implementation was not read-only. If you do not wish to
  182. // forward modifying operations to your data, you may opt to leave these
  183. // functions empty. To make this easier, you can include the
  184. // TableContentReadOnly type in your struct. See also the
  185. // demos/table/virtualtable example.
  186. // Set the cell at the given position to the provided cell.
  187. SetCell(row, column int, cell *TableCell)
  188. // Remove the row at the given position by shifting all following rows up
  189. // by one. Out of range positions may be ignored.
  190. RemoveRow(row int)
  191. // Remove the column at the given position by shifting all following columns
  192. // left by one. Out of range positions may be ignored.
  193. RemoveColumn(column int)
  194. // Insert a new empty row at the given position by shifting all rows at that
  195. // position and below down by one. Implementers may decide what to do with
  196. // out of range positions.
  197. InsertRow(row int)
  198. // Insert a new empty column at the given position by shifting all columns
  199. // at that position and to the right by one to the right. Implementers may
  200. // decide what to do with out of range positions.
  201. InsertColumn(column int)
  202. // Remove all table data.
  203. Clear()
  204. }
  205. // TableContentReadOnly is an empty struct which implements the write operations
  206. // of the TableContent interface. None of the implemented functions do anything.
  207. // You can embed this struct into your own structs to free yourself from having
  208. // to implement the empty write functions of TableContent. See
  209. // demos/table/virtualtable for an example.
  210. type TableContentReadOnly struct{}
  211. // SetCell does not do anything.
  212. func (t TableContentReadOnly) SetCell(row, column int, cell *TableCell) {
  213. // nop.
  214. }
  215. // RemoveRow does not do anything.
  216. func (t TableContentReadOnly) RemoveRow(row int) {
  217. // nop.
  218. }
  219. // RemoveColumn does not do anything.
  220. func (t TableContentReadOnly) RemoveColumn(column int) {
  221. // nop.
  222. }
  223. // InsertRow does not do anything.
  224. func (t TableContentReadOnly) InsertRow(row int) {
  225. // nop.
  226. }
  227. // InsertColumn does not do anything.
  228. func (t TableContentReadOnly) InsertColumn(column int) {
  229. // nop.
  230. }
  231. // Clear does not do anything.
  232. func (t TableContentReadOnly) Clear() {
  233. // nop.
  234. }
  235. // tableDefaultContent implements the default TableContent interface for the
  236. // Table class.
  237. type tableDefaultContent struct {
  238. // The cells of the table. Rows first, then columns.
  239. cells [][]*TableCell
  240. // The rightmost column in the data set.
  241. lastColumn int
  242. }
  243. // Clear clears all data.
  244. func (t *tableDefaultContent) Clear() {
  245. t.cells = nil
  246. t.lastColumn = -1
  247. }
  248. // SetCell sets a cell's content.
  249. func (t *tableDefaultContent) SetCell(row, column int, cell *TableCell) {
  250. if row >= len(t.cells) {
  251. t.cells = append(t.cells, make([][]*TableCell, row-len(t.cells)+1)...)
  252. }
  253. rowLen := len(t.cells[row])
  254. if column >= rowLen {
  255. t.cells[row] = append(t.cells[row], make([]*TableCell, column-rowLen+1)...)
  256. for c := rowLen; c < column; c++ {
  257. t.cells[row][c] = &TableCell{}
  258. }
  259. }
  260. t.cells[row][column] = cell
  261. if column > t.lastColumn {
  262. t.lastColumn = column
  263. }
  264. }
  265. // RemoveRow removes a row from the data.
  266. func (t *tableDefaultContent) RemoveRow(row int) {
  267. if row < 0 || row >= len(t.cells) {
  268. return
  269. }
  270. t.cells = append(t.cells[:row], t.cells[row+1:]...)
  271. }
  272. // RemoveColumn removes a column from the data.
  273. func (t *tableDefaultContent) RemoveColumn(column int) {
  274. for row := range t.cells {
  275. if column < 0 || column >= len(t.cells[row]) {
  276. continue
  277. }
  278. t.cells[row] = append(t.cells[row][:column], t.cells[row][column+1:]...)
  279. }
  280. if column >= 0 && column <= t.lastColumn {
  281. t.lastColumn--
  282. }
  283. }
  284. // InsertRow inserts a new row at the given position.
  285. func (t *tableDefaultContent) InsertRow(row int) {
  286. if row >= len(t.cells) {
  287. return
  288. }
  289. t.cells = append(t.cells, nil) // Extend by one.
  290. copy(t.cells[row+1:], t.cells[row:]) // Shift down.
  291. t.cells[row] = nil // New row is uninitialized.
  292. }
  293. // InsertColumn inserts a new column at the given position.
  294. func (t *tableDefaultContent) InsertColumn(column int) {
  295. for row := range t.cells {
  296. if column >= len(t.cells[row]) {
  297. continue
  298. }
  299. t.cells[row] = append(t.cells[row], nil) // Extend by one.
  300. copy(t.cells[row][column+1:], t.cells[row][column:]) // Shift to the right.
  301. t.cells[row][column] = &TableCell{} // New element is an uninitialized table cell.
  302. }
  303. }
  304. // GetCell returns the cell at the given position.
  305. func (t *tableDefaultContent) GetCell(row, column int) *TableCell {
  306. if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) {
  307. return nil
  308. }
  309. return t.cells[row][column]
  310. }
  311. // GetRowCount returns the number of rows in the data set.
  312. func (t *tableDefaultContent) GetRowCount() int {
  313. return len(t.cells)
  314. }
  315. // GetColumnCount returns the number of columns in the data set.
  316. func (t *tableDefaultContent) GetColumnCount() int {
  317. if len(t.cells) == 0 {
  318. return 0
  319. }
  320. return t.lastColumn + 1
  321. }
  322. // Table visualizes two-dimensional data consisting of rows and columns. Each
  323. // Table cell is defined via SetCell() by the TableCell type. They can be added
  324. // dynamically to the table and changed any time.
  325. //
  326. // The most compact display of a table is without borders. Each row will then
  327. // occupy one row on screen and columns are separated by the rune defined via
  328. // SetSeparator() (a space character by default).
  329. //
  330. // When borders are turned on (via SetBorders()), each table cell is surrounded
  331. // by lines. Therefore one table row will require two rows on screen.
  332. //
  333. // Columns will use as much horizontal space as they need. You can constrain
  334. // their size with the MaxWidth parameter of the TableCell type.
  335. //
  336. // # Fixed Columns
  337. //
  338. // You can define fixed rows and rolumns via SetFixed(). They will always stay
  339. // in their place, even when the table is scrolled. Fixed rows are always the
  340. // top rows. Fixed columns are always the leftmost columns.
  341. //
  342. // # Selections
  343. //
  344. // You can call SetSelectable() to set columns and/or rows to "selectable". If
  345. // the flag is set only for columns, entire columns can be selected by the user.
  346. // If it is set only for rows, entire rows can be selected. If both flags are
  347. // set, individual cells can be selected. The "selected" handler set via
  348. // SetSelectedFunc() is invoked when the user presses Enter on a selection.
  349. //
  350. // # Navigation
  351. //
  352. // If the table extends beyond the available space, it can be navigated with
  353. // key bindings similar to Vim:
  354. //
  355. // - h, left arrow: Move left by one column.
  356. // - l, right arrow: Move right by one column.
  357. // - j, down arrow: Move down by one row.
  358. // - k, up arrow: Move up by one row.
  359. // - g, home: Move to the top.
  360. // - G, end: Move to the bottom.
  361. // - Ctrl-F, page down: Move down by one page.
  362. // - Ctrl-B, page up: Move up by one page.
  363. //
  364. // When there is no selection, this affects the entire table (except for fixed
  365. // rows and columns). When there is a selection, the user moves the selection.
  366. // The class will attempt to keep the selection from moving out of the screen.
  367. //
  368. // Use SetInputCapture() to override or modify keyboard input.
  369. //
  370. // See https://github.com/rivo/tview/wiki/Table for an example.
  371. type Table struct {
  372. *Box
  373. // Whether or not this table has borders around each cell.
  374. borders bool
  375. // The color of the borders or the separator.
  376. bordersColor tcell.Color
  377. // If there are no borders, the column separator.
  378. separator rune
  379. // The table's data structure.
  380. content TableContent
  381. // If true, when calculating the widths of the columns, all rows are evaluated
  382. // instead of only the visible ones.
  383. evaluateAllRows bool
  384. // The number of fixed rows / columns.
  385. fixedRows, fixedColumns int
  386. // Whether or not rows or columns can be selected. If both are set to true,
  387. // cells can be selected.
  388. rowsSelectable, columnsSelectable bool
  389. // The currently selected row and column.
  390. selectedRow, selectedColumn int
  391. // A temporary flag which causes the next call to Draw() to force the
  392. // current selection to remain visible. It is set to false afterwards.
  393. clampToSelection bool
  394. // If set to true, moving the selection will wrap around horizontally (last
  395. // to first column and vice versa) or vertically (last to first row and vice
  396. // versa).
  397. wrapHorizontally, wrapVertically bool
  398. // The number of rows/columns by which the table is scrolled down/to the
  399. // right.
  400. rowOffset, columnOffset int
  401. // If set to true, the table's last row will always be visible.
  402. trackEnd bool
  403. // The number of visible rows the last time the table was drawn.
  404. visibleRows int
  405. // The indices of the visible columns as of the last time the table was drawn.
  406. visibleColumnIndices []int
  407. // The net widths of the visible columns as of the last time the table was
  408. // drawn.
  409. visibleColumnWidths []int
  410. // The style of the selected rows. If this value is the empty struct,
  411. // selected rows are simply inverted.
  412. selectedStyle tcell.Style
  413. // An optional function which gets called when the user presses Enter on a
  414. // selected cell. If entire rows selected, the column value is undefined.
  415. // Likewise for entire columns.
  416. selected func(row, column int)
  417. // An optional function which gets called when the user changes the selection.
  418. // If entire rows selected, the column value is undefined.
  419. // Likewise for entire columns.
  420. selectionChanged func(row, column int)
  421. // An optional function which gets called when the user presses Escape, Tab,
  422. // or Backtab. Also when the user presses Enter if nothing is selectable.
  423. done func(key tcell.Key)
  424. }
  425. // NewTable returns a new table.
  426. func NewTable() *Table {
  427. t := &Table{
  428. Box: NewBox(),
  429. bordersColor: Styles.GraphicsColor,
  430. separator: ' ',
  431. }
  432. t.SetContent(nil)
  433. return t
  434. }
  435. // SetContent sets a new content type for this table. This allows you to back
  436. // the table by a data structure of your own, for example one that cannot be
  437. // fully held in memory. For details, see the TableContent interface
  438. // documentation.
  439. //
  440. // A value of nil will return the table to its default implementation where all
  441. // of its table cells are kept in memory.
  442. func (t *Table) SetContent(content TableContent) *Table {
  443. if content != nil {
  444. t.content = content
  445. } else {
  446. t.content = &tableDefaultContent{
  447. lastColumn: -1,
  448. }
  449. }
  450. return t
  451. }
  452. // Clear removes all table data.
  453. func (t *Table) Clear() *Table {
  454. t.content.Clear()
  455. return t
  456. }
  457. // SetBorders sets whether or not each cell in the table is surrounded by a
  458. // border.
  459. func (t *Table) SetBorders(show bool) *Table {
  460. t.borders = show
  461. return t
  462. }
  463. // SetBordersColor sets the color of the cell borders.
  464. func (t *Table) SetBordersColor(color tcell.Color) *Table {
  465. t.bordersColor = color
  466. return t
  467. }
  468. // SetSelectedStyle sets a specific style for selected cells. If no such style
  469. // is set, per default, selected cells are inverted (i.e. their foreground and
  470. // background colors are swapped).
  471. //
  472. // To reset a previous setting to its default, make the following call:
  473. //
  474. // table.SetSelectedStyle(tcell.Style{})
  475. func (t *Table) SetSelectedStyle(style tcell.Style) *Table {
  476. t.selectedStyle = style
  477. return t
  478. }
  479. // SetSeparator sets the character used to fill the space between two
  480. // neighboring cells. This is a space character ' ' per default but you may
  481. // want to set it to Borders.Vertical (or any other rune) if the column
  482. // separation should be more visible. If cell borders are activated, this is
  483. // ignored.
  484. //
  485. // Separators have the same color as borders.
  486. func (t *Table) SetSeparator(separator rune) *Table {
  487. t.separator = separator
  488. return t
  489. }
  490. // SetFixed sets the number of fixed rows and columns which are always visible
  491. // even when the rest of the cells are scrolled out of view. Rows are always the
  492. // top-most ones. Columns are always the left-most ones.
  493. func (t *Table) SetFixed(rows, columns int) *Table {
  494. t.fixedRows, t.fixedColumns = rows, columns
  495. return t
  496. }
  497. // SetSelectable sets the flags which determine what can be selected in a table.
  498. // There are three selection modi:
  499. //
  500. // - rows = false, columns = false: Nothing can be selected.
  501. // - rows = true, columns = false: Rows can be selected.
  502. // - rows = false, columns = true: Columns can be selected.
  503. // - rows = true, columns = true: Individual cells can be selected.
  504. func (t *Table) SetSelectable(rows, columns bool) *Table {
  505. t.rowsSelectable, t.columnsSelectable = rows, columns
  506. return t
  507. }
  508. // GetSelectable returns what can be selected in a table. Refer to
  509. // SetSelectable() for details.
  510. func (t *Table) GetSelectable() (rows, columns bool) {
  511. return t.rowsSelectable, t.columnsSelectable
  512. }
  513. // GetSelection returns the position of the current selection.
  514. // If entire rows are selected, the column index is undefined.
  515. // Likewise for entire columns.
  516. func (t *Table) GetSelection() (row, column int) {
  517. return t.selectedRow, t.selectedColumn
  518. }
  519. // Select sets the selected cell. Depending on the selection settings
  520. // specified via SetSelectable(), this may be an entire row or column, or even
  521. // ignored completely. The "selection changed" event is fired if such a callback
  522. // is available (even if the selection ends up being the same as before and even
  523. // if cells are not selectable).
  524. func (t *Table) Select(row, column int) *Table {
  525. t.selectedRow, t.selectedColumn = row, column
  526. t.clampToSelection = true
  527. if t.selectionChanged != nil {
  528. t.selectionChanged(row, column)
  529. }
  530. return t
  531. }
  532. // SetOffset sets how many rows and columns should be skipped when drawing the
  533. // table. This is useful for large tables that do not fit on the screen.
  534. // Navigating a selection can change these values.
  535. //
  536. // Fixed rows and columns are never skipped.
  537. func (t *Table) SetOffset(row, column int) *Table {
  538. t.rowOffset, t.columnOffset = row, column
  539. t.trackEnd = false
  540. return t
  541. }
  542. // GetOffset returns the current row and column offset. This indicates how many
  543. // rows and columns the table is scrolled down and to the right.
  544. func (t *Table) GetOffset() (row, column int) {
  545. return t.rowOffset, t.columnOffset
  546. }
  547. // SetEvaluateAllRows sets a flag which determines the rows to be evaluated when
  548. // calculating the widths of the table's columns. When false, only visible rows
  549. // are evaluated. When true, all rows in the table are evaluated.
  550. //
  551. // Set this flag to true to avoid shifting column widths when the table is
  552. // scrolled. (May come with a performance penalty for large tables.)
  553. //
  554. // Use with caution on very large tables, especially those not backed by the
  555. // default TableContent data structure.
  556. func (t *Table) SetEvaluateAllRows(all bool) *Table {
  557. t.evaluateAllRows = all
  558. return t
  559. }
  560. // SetSelectedFunc sets a handler which is called whenever the user presses the
  561. // Enter key on a selected cell/row/column. The handler receives the position of
  562. // the selection and its cell contents. If entire rows are selected, the column
  563. // index is undefined. Likewise for entire columns.
  564. func (t *Table) SetSelectedFunc(handler func(row, column int)) *Table {
  565. t.selected = handler
  566. return t
  567. }
  568. // SetSelectionChangedFunc sets a handler which is called whenever the current
  569. // selection changes. The handler receives the position of the new selection.
  570. // If entire rows are selected, the column index is undefined. Likewise for
  571. // entire columns.
  572. func (t *Table) SetSelectionChangedFunc(handler func(row, column int)) *Table {
  573. t.selectionChanged = handler
  574. return t
  575. }
  576. // SetDoneFunc sets a handler which is called whenever the user presses the
  577. // Escape, Tab, or Backtab key. If nothing is selected, it is also called when
  578. // user presses the Enter key (because pressing Enter on a selection triggers
  579. // the "selected" handler set via SetSelectedFunc()).
  580. func (t *Table) SetDoneFunc(handler func(key tcell.Key)) *Table {
  581. t.done = handler
  582. return t
  583. }
  584. // SetCell sets the content of a cell the specified position. It is ok to
  585. // directly instantiate a TableCell object. If the cell has content, at least
  586. // the Text and Color fields should be set.
  587. //
  588. // Note that setting cells in previously unknown rows and columns will
  589. // automatically extend the internal table representation with empty TableCell
  590. // objects, e.g. starting with a row of 100,000 will immediately create 100,000
  591. // empty rows.
  592. //
  593. // To avoid unnecessary garbage collection, fill columns from left to right.
  594. func (t *Table) SetCell(row, column int, cell *TableCell) *Table {
  595. t.content.SetCell(row, column, cell)
  596. return t
  597. }
  598. // SetCellSimple calls SetCell() with the given text, left-aligned, in white.
  599. func (t *Table) SetCellSimple(row, column int, text string) *Table {
  600. t.SetCell(row, column, NewTableCell(text))
  601. return t
  602. }
  603. // GetCell returns the contents of the cell at the specified position. A valid
  604. // TableCell object is always returned but it will be uninitialized if the cell
  605. // was not previously set. Such an uninitialized object will not automatically
  606. // be inserted. Therefore, repeated calls to this function may return different
  607. // pointers for uninitialized cells.
  608. func (t *Table) GetCell(row, column int) *TableCell {
  609. cell := t.content.GetCell(row, column)
  610. if cell == nil {
  611. cell = &TableCell{}
  612. }
  613. return cell
  614. }
  615. // RemoveRow removes the row at the given position from the table. If there is
  616. // no such row, this has no effect.
  617. func (t *Table) RemoveRow(row int) *Table {
  618. t.content.RemoveRow(row)
  619. return t
  620. }
  621. // RemoveColumn removes the column at the given position from the table. If
  622. // there is no such column, this has no effect.
  623. func (t *Table) RemoveColumn(column int) *Table {
  624. t.content.RemoveColumn(column)
  625. return t
  626. }
  627. // InsertRow inserts a row before the row with the given index. Cells on the
  628. // given row and below will be shifted to the bottom by one row. If "row" is
  629. // equal or larger than the current number of rows, this function has no effect.
  630. func (t *Table) InsertRow(row int) *Table {
  631. t.content.InsertRow(row)
  632. return t
  633. }
  634. // InsertColumn inserts a column before the column with the given index. Cells
  635. // in the given column and to its right will be shifted to the right by one
  636. // column. Rows that have fewer initialized cells than "column" will remain
  637. // unchanged.
  638. func (t *Table) InsertColumn(column int) *Table {
  639. t.content.InsertColumn(column)
  640. return t
  641. }
  642. // GetRowCount returns the number of rows in the table.
  643. func (t *Table) GetRowCount() int {
  644. return t.content.GetRowCount()
  645. }
  646. // GetColumnCount returns the (maximum) number of columns in the table.
  647. func (t *Table) GetColumnCount() int {
  648. return t.content.GetColumnCount()
  649. }
  650. // cellAt returns the row and column located at the given screen coordinates.
  651. // Each returned value may be negative if there is no row and/or cell. This
  652. // function will also process coordinates outside the table's inner rectangle so
  653. // callers will need to check for bounds themselves.
  654. func (t *Table) cellAt(x, y int) (row, column int) {
  655. rectX, rectY, _, _ := t.GetInnerRect()
  656. // Determine row as seen on screen.
  657. if t.borders {
  658. row = (y - rectY - 1) / 2
  659. } else {
  660. row = y - rectY
  661. }
  662. // Respect fixed rows and row offset.
  663. if row >= 0 {
  664. if row >= t.fixedRows {
  665. row += t.rowOffset
  666. }
  667. if row >= t.content.GetRowCount() {
  668. row = -1
  669. }
  670. }
  671. // Saerch for the clicked column.
  672. column = -1
  673. if x >= rectX {
  674. columnX := rectX
  675. if t.borders {
  676. columnX++
  677. }
  678. for index, width := range t.visibleColumnWidths {
  679. columnX += width + 1
  680. if x < columnX {
  681. column = t.visibleColumnIndices[index]
  682. break
  683. }
  684. }
  685. }
  686. return
  687. }
  688. // ScrollToBeginning scrolls the table to the beginning to that the top left
  689. // corner of the table is shown. Note that this position may be corrected if
  690. // there is a selection.
  691. func (t *Table) ScrollToBeginning() *Table {
  692. t.trackEnd = false
  693. t.columnOffset = 0
  694. t.rowOffset = 0
  695. return t
  696. }
  697. // ScrollToEnd scrolls the table to the beginning to that the bottom left corner
  698. // of the table is shown. Adding more rows to the table will cause it to
  699. // automatically scroll with the new data. Note that this position may be
  700. // corrected if there is a selection.
  701. func (t *Table) ScrollToEnd() *Table {
  702. t.trackEnd = true
  703. t.columnOffset = 0
  704. t.rowOffset = t.content.GetRowCount()
  705. return t
  706. }
  707. // SetWrapSelection determines whether a selection wraps vertically or
  708. // horizontally when moved. Vertically wrapping selections will jump from the
  709. // last selectable row to the first selectable row and vice versa. Horizontally
  710. // wrapping selections will jump from the last selectable column to the first
  711. // selectable column (on the next selectable row) or from the first selectable
  712. // column to the last selectable column (on the previous selectable row). If set
  713. // to false, the selection is not moved when it is already on the first/last
  714. // selectable row/column.
  715. //
  716. // The default is for both values to be false.
  717. func (t *Table) SetWrapSelection(vertical, horizontal bool) *Table {
  718. t.wrapHorizontally = horizontal
  719. t.wrapVertically = vertical
  720. return t
  721. }
  722. // Draw draws this primitive onto the screen.
  723. func (t *Table) Draw(screen tcell.Screen) {
  724. t.Box.DrawForSubclass(screen, t)
  725. // What's our available screen space?
  726. _, totalHeight := screen.Size()
  727. x, y, width, height := t.GetInnerRect()
  728. netWidth := width
  729. if t.borders {
  730. t.visibleRows = height / 2
  731. netWidth -= 2
  732. } else {
  733. t.visibleRows = height
  734. }
  735. // If this cell is not selectable, find the next one.
  736. rowCount, columnCount := t.content.GetRowCount(), t.content.GetColumnCount()
  737. if t.rowsSelectable || t.columnsSelectable {
  738. if t.selectedColumn < 0 {
  739. t.selectedColumn = 0
  740. }
  741. if t.selectedRow < 0 {
  742. t.selectedRow = 0
  743. }
  744. for t.selectedRow < rowCount {
  745. cell := t.content.GetCell(t.selectedRow, t.selectedColumn)
  746. if cell != nil && !cell.NotSelectable {
  747. break
  748. }
  749. t.selectedColumn++
  750. if t.selectedColumn > columnCount-1 {
  751. t.selectedColumn = 0
  752. t.selectedRow++
  753. }
  754. }
  755. }
  756. // Clamp row offsets if requested.
  757. defer func() {
  758. t.clampToSelection = false // Only once.
  759. }()
  760. if t.clampToSelection && t.rowsSelectable {
  761. if t.selectedRow >= t.fixedRows && t.selectedRow < t.fixedRows+t.rowOffset {
  762. t.rowOffset = t.selectedRow - t.fixedRows
  763. t.trackEnd = false
  764. }
  765. if t.borders {
  766. if t.selectedRow+1-t.rowOffset >= height/2 {
  767. t.rowOffset = t.selectedRow + 1 - height/2
  768. t.trackEnd = false
  769. }
  770. } else {
  771. if t.selectedRow+1-t.rowOffset >= height {
  772. t.rowOffset = t.selectedRow + 1 - height
  773. t.trackEnd = false
  774. }
  775. }
  776. }
  777. if t.rowOffset < 0 {
  778. t.rowOffset = 0
  779. }
  780. if t.borders {
  781. if rowCount-t.rowOffset < height/2 {
  782. t.trackEnd = true
  783. }
  784. } else {
  785. if rowCount-t.rowOffset < height {
  786. t.trackEnd = true
  787. }
  788. }
  789. if t.trackEnd {
  790. if t.borders {
  791. t.rowOffset = rowCount - height/2
  792. } else {
  793. t.rowOffset = rowCount - height
  794. }
  795. }
  796. if t.rowOffset < 0 {
  797. t.rowOffset = 0
  798. }
  799. // Avoid invalid column offsets.
  800. if t.columnOffset >= columnCount-t.fixedColumns {
  801. t.columnOffset = columnCount - t.fixedColumns - 1
  802. }
  803. if t.columnOffset < 0 {
  804. t.columnOffset = 0
  805. }
  806. // Determine the indices of the rows which fit on the screen.
  807. var (
  808. rows, allRows []int
  809. tableHeight int
  810. )
  811. rowStep := 1
  812. if t.borders {
  813. rowStep = 2 // With borders, every table row takes two screen rows.
  814. }
  815. if t.evaluateAllRows {
  816. allRows = make([]int, rowCount)
  817. for row := 0; row < rowCount; row++ {
  818. allRows[row] = row
  819. }
  820. }
  821. indexRow := func(row int) bool { // Determine if this row is visible, store its index.
  822. if tableHeight >= height {
  823. return false
  824. }
  825. rows = append(rows, row)
  826. tableHeight += rowStep
  827. return true
  828. }
  829. for row := 0; row < t.fixedRows && row < rowCount; row++ { // Do the fixed rows first.
  830. if !indexRow(row) {
  831. break
  832. }
  833. }
  834. for row := t.fixedRows + t.rowOffset; row < rowCount; row++ { // Then the remaining rows.
  835. if !indexRow(row) {
  836. break
  837. }
  838. }
  839. // Determine the columns' indices, widths, and expansion values that fit on
  840. // the screen.
  841. var (
  842. tableWidth, expansionTotal int
  843. columns, widths, expansions []int
  844. )
  845. includesSelection := !t.clampToSelection || !t.columnsSelectable
  846. // Helper function that evaluates one column. Returns true if the column
  847. // didn't fit at all.
  848. indexColumn := func(column int) bool {
  849. if netWidth == 0 || tableWidth >= netWidth {
  850. return true
  851. }
  852. var maxWidth, expansion int
  853. evaluationRows := rows
  854. if t.evaluateAllRows {
  855. evaluationRows = allRows
  856. }
  857. for _, row := range evaluationRows {
  858. if cell := t.content.GetCell(row, column); cell != nil {
  859. _, _, _, _, _, _, cellWidth := decomposeString(cell.Text, true, false)
  860. if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth {
  861. cellWidth = cell.MaxWidth
  862. }
  863. if cellWidth > maxWidth {
  864. maxWidth = cellWidth
  865. }
  866. if cell.Expansion > expansion {
  867. expansion = cell.Expansion
  868. }
  869. }
  870. }
  871. clampedMaxWidth := maxWidth
  872. if tableWidth+maxWidth > netWidth {
  873. clampedMaxWidth = netWidth - tableWidth
  874. }
  875. columns = append(columns, column)
  876. widths = append(widths, clampedMaxWidth)
  877. expansions = append(expansions, expansion)
  878. tableWidth += clampedMaxWidth + 1
  879. expansionTotal += expansion
  880. if t.columnsSelectable && t.clampToSelection && column == t.selectedColumn {
  881. // We want selections to appear fully.
  882. includesSelection = clampedMaxWidth == maxWidth
  883. }
  884. return false
  885. }
  886. // Helper function that evaluates multiple columns, starting at "start" and
  887. // at most ending at "maxEnd". Returns first column not included anymore (or
  888. // -1 if all are included).
  889. indexColumns := func(start, maxEnd int) int {
  890. if start == maxEnd {
  891. return -1
  892. }
  893. if start < maxEnd {
  894. // Forward-evaluate columns.
  895. for column := start; column < maxEnd; column++ {
  896. if indexColumn(column) {
  897. return column
  898. }
  899. }
  900. return -1
  901. }
  902. // Backward-evaluate columns.
  903. startLen := len(columns)
  904. defer func() {
  905. // Becaue we went backwards, we must reverse the partial slices.
  906. for i, j := startLen, len(columns)-1; i < j; i, j = i+1, j-1 {
  907. columns[i], columns[j] = columns[j], columns[i]
  908. widths[i], widths[j] = widths[j], widths[i]
  909. expansions[i], expansions[j] = expansions[j], expansions[i]
  910. }
  911. }()
  912. for column := start; column >= maxEnd; column-- {
  913. if indexColumn(column) {
  914. return column
  915. }
  916. }
  917. return -1
  918. }
  919. // Reset the table to only its fixed columns.
  920. var fixedTableWidth, fixedExpansionTotal int
  921. resetColumns := func() {
  922. tableWidth = fixedTableWidth
  923. expansionTotal = fixedExpansionTotal
  924. columns = columns[:t.fixedColumns]
  925. widths = widths[:t.fixedColumns]
  926. expansions = expansions[:t.fixedColumns]
  927. }
  928. // Add fixed columns.
  929. if indexColumns(0, t.fixedColumns) < 0 {
  930. fixedTableWidth = tableWidth
  931. fixedExpansionTotal = expansionTotal
  932. // Add unclamped columns.
  933. if column := indexColumns(t.fixedColumns+t.columnOffset, columnCount); !includesSelection || column < 0 && t.columnOffset > 0 {
  934. // Offset is not optimal. Try again.
  935. if !includesSelection {
  936. // Clamp to selection.
  937. resetColumns()
  938. if t.selectedColumn <= t.fixedColumns+t.columnOffset {
  939. // It's on the left. Start with the selection.
  940. t.columnOffset = t.selectedColumn - t.fixedColumns
  941. indexColumns(t.fixedColumns+t.columnOffset, columnCount)
  942. } else {
  943. // It's on the right. End with the selection.
  944. if column := indexColumns(t.selectedColumn, t.fixedColumns); column >= 0 {
  945. t.columnOffset = column + 1 - t.fixedColumns
  946. } else {
  947. t.columnOffset = 0
  948. }
  949. }
  950. } else if tableWidth < netWidth {
  951. // Don't waste space. Try to fit as much on screen as possible.
  952. resetColumns()
  953. if column := indexColumns(columnCount-1, t.fixedColumns); column >= 0 {
  954. t.columnOffset = column + 1 - t.fixedColumns
  955. } else {
  956. t.columnOffset = 0
  957. }
  958. }
  959. }
  960. }
  961. // If we have space left, distribute it.
  962. if tableWidth < netWidth {
  963. toDistribute := netWidth - tableWidth
  964. for index, expansion := range expansions {
  965. if expansionTotal <= 0 {
  966. break
  967. }
  968. expWidth := toDistribute * expansion / expansionTotal
  969. widths[index] += expWidth
  970. toDistribute -= expWidth
  971. expansionTotal -= expansion
  972. }
  973. }
  974. // Helper function which draws border runes.
  975. borderStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.bordersColor)
  976. drawBorder := func(colX, rowY int, ch rune) {
  977. screen.SetContent(x+colX, y+rowY, ch, nil, borderStyle)
  978. }
  979. // Draw the cells (and borders).
  980. var columnX int
  981. if t.borders {
  982. columnX++
  983. }
  984. for columnIndex, column := range columns {
  985. columnWidth := widths[columnIndex]
  986. for rowY, row := range rows {
  987. if t.borders {
  988. // Draw borders.
  989. rowY *= 2
  990. for pos := 0; pos < columnWidth && columnX+pos < width; pos++ {
  991. drawBorder(columnX+pos, rowY, Borders.Horizontal)
  992. }
  993. ch := Borders.Cross
  994. if row == 0 {
  995. if column == 0 {
  996. ch = Borders.TopLeft
  997. } else {
  998. ch = Borders.TopT
  999. }
  1000. } else if column == 0 {
  1001. ch = Borders.LeftT
  1002. }
  1003. drawBorder(columnX-1, rowY, ch)
  1004. rowY++
  1005. if rowY >= height || y+rowY >= totalHeight {
  1006. break // No space for the text anymore.
  1007. }
  1008. drawBorder(columnX-1, rowY, Borders.Vertical)
  1009. } else if columnIndex < len(columns)-1 {
  1010. // Draw separator.
  1011. drawBorder(columnX+columnWidth, rowY, t.separator)
  1012. }
  1013. // Get the cell.
  1014. cell := t.content.GetCell(row, column)
  1015. if cell == nil {
  1016. continue
  1017. }
  1018. // Draw text.
  1019. finalWidth := columnWidth
  1020. if columnX+columnWidth >= width {
  1021. finalWidth = width - columnX
  1022. }
  1023. cell.x, cell.y, cell.width = x+columnX, y+rowY, finalWidth
  1024. _, printed, _, _ := printWithStyle(screen, cell.Text, x+columnX, y+rowY, 0, finalWidth, cell.Align, tcell.StyleDefault.Foreground(cell.Color).Attributes(cell.Attributes), true)
  1025. if TaggedStringWidth(cell.Text)-printed > 0 && printed > 0 {
  1026. _, _, style, _ := screen.GetContent(x+columnX+finalWidth-1, y+rowY)
  1027. printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+finalWidth-1, y+rowY, 0, 1, AlignLeft, style, false)
  1028. }
  1029. }
  1030. // Draw bottom border.
  1031. if rowY := 2 * len(rows); t.borders && rowY > 0 && rowY < height {
  1032. for pos := 0; pos < columnWidth && columnX+1+pos < width; pos++ {
  1033. drawBorder(columnX+pos, rowY, Borders.Horizontal)
  1034. }
  1035. ch := Borders.Cross
  1036. if rows[len(rows)-1] == rowCount-1 {
  1037. if column == 0 {
  1038. ch = Borders.BottomLeft
  1039. } else {
  1040. ch = Borders.BottomT
  1041. }
  1042. } else if column == 0 {
  1043. ch = Borders.BottomLeft
  1044. }
  1045. drawBorder(columnX-1, rowY, ch)
  1046. }
  1047. columnX += columnWidth + 1
  1048. }
  1049. // Draw right border.
  1050. columnX--
  1051. if t.borders && len(rows) > 0 && len(columns) > 0 && columnX < width {
  1052. lastColumn := columns[len(columns)-1] == columnCount-1
  1053. for rowY := range rows {
  1054. rowY *= 2
  1055. if rowY+1 < height {
  1056. drawBorder(columnX, rowY+1, Borders.Vertical)
  1057. }
  1058. ch := Borders.Cross
  1059. if rowY == 0 {
  1060. if lastColumn {
  1061. ch = Borders.TopRight
  1062. } else {
  1063. ch = Borders.TopT
  1064. }
  1065. } else if lastColumn {
  1066. ch = Borders.RightT
  1067. }
  1068. drawBorder(columnX, rowY, ch)
  1069. }
  1070. if rowY := 2 * len(rows); rowY < height {
  1071. ch := Borders.BottomT
  1072. if lastColumn {
  1073. ch = Borders.BottomRight
  1074. }
  1075. drawBorder(columnX, rowY, ch)
  1076. }
  1077. }
  1078. // Helper function which colors the background of a box.
  1079. // backgroundTransparent == true => Don't modify background color (when invert == false).
  1080. // textTransparent == true => Don't modify text color (when invert == false).
  1081. // attr == 0 => Don't change attributes.
  1082. // invert == true => Ignore attr, set text to backgroundColor or t.backgroundColor;
  1083. // set background to textColor.
  1084. colorBackground := func(fromX, fromY, w, h int, backgroundColor, textColor tcell.Color, backgroundTransparent, textTransparent bool, attr tcell.AttrMask, invert bool) {
  1085. for by := 0; by < h && fromY+by < y+height; by++ {
  1086. for bx := 0; bx < w && fromX+bx < x+width; bx++ {
  1087. m, c, style, _ := screen.GetContent(fromX+bx, fromY+by)
  1088. fg, bg, a := style.Decompose()
  1089. if invert {
  1090. style = style.Background(textColor).Foreground(backgroundColor)
  1091. } else {
  1092. if !backgroundTransparent {
  1093. bg = backgroundColor
  1094. }
  1095. if !textTransparent {
  1096. fg = textColor
  1097. }
  1098. if attr != 0 {
  1099. a = attr
  1100. }
  1101. style = style.Background(bg).Foreground(fg).Attributes(a)
  1102. }
  1103. screen.SetContent(fromX+bx, fromY+by, m, c, style)
  1104. }
  1105. }
  1106. }
  1107. // Color the cell backgrounds. To avoid undesirable artefacts, we combine
  1108. // the drawing of a cell by background color, selected cells last.
  1109. type cellInfo struct {
  1110. x, y, w, h int
  1111. cell *TableCell
  1112. selected bool
  1113. }
  1114. cellsByBackgroundColor := make(map[tcell.Color][]*cellInfo)
  1115. var backgroundColors []tcell.Color
  1116. for rowY, row := range rows {
  1117. columnX := 0
  1118. rowSelected := t.rowsSelectable && !t.columnsSelectable && row == t.selectedRow
  1119. for columnIndex, column := range columns {
  1120. columnWidth := widths[columnIndex]
  1121. cell := t.content.GetCell(row, column)
  1122. if cell == nil {
  1123. continue
  1124. }
  1125. bx, by, bw, bh := x+columnX, y+rowY, columnWidth+1, 1
  1126. if t.borders {
  1127. by = y + rowY*2
  1128. bw++
  1129. bh = 3
  1130. }
  1131. columnSelected := t.columnsSelectable && !t.rowsSelectable && column == t.selectedColumn
  1132. cellSelected := !cell.NotSelectable && (columnSelected || rowSelected || t.rowsSelectable && t.columnsSelectable && column == t.selectedColumn && row == t.selectedRow)
  1133. entries, ok := cellsByBackgroundColor[cell.BackgroundColor]
  1134. cellsByBackgroundColor[cell.BackgroundColor] = append(entries, &cellInfo{
  1135. x: bx,
  1136. y: by,
  1137. w: bw,
  1138. h: bh,
  1139. cell: cell,
  1140. selected: cellSelected,
  1141. })
  1142. if !ok {
  1143. backgroundColors = append(backgroundColors, cell.BackgroundColor)
  1144. }
  1145. columnX += columnWidth + 1
  1146. }
  1147. }
  1148. sort.Slice(backgroundColors, func(i int, j int) bool {
  1149. // Draw brightest colors last (i.e. on top).
  1150. r, g, b := backgroundColors[i].RGB()
  1151. c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255}
  1152. _, _, li := c.Hcl()
  1153. r, g, b = backgroundColors[j].RGB()
  1154. c = colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255}
  1155. _, _, lj := c.Hcl()
  1156. return li < lj
  1157. })
  1158. selFg, selBg, selAttr := t.selectedStyle.Decompose()
  1159. for _, bgColor := range backgroundColors {
  1160. entries := cellsByBackgroundColor[bgColor]
  1161. for _, info := range entries {
  1162. if info.selected {
  1163. if t.selectedStyle != (tcell.Style{}) {
  1164. defer colorBackground(info.x, info.y, info.w, info.h, selBg, selFg, false, false, selAttr, false)
  1165. } else {
  1166. defer colorBackground(info.x, info.y, info.w, info.h, bgColor, info.cell.Color, false, false, 0, true)
  1167. }
  1168. } else {
  1169. colorBackground(info.x, info.y, info.w, info.h, bgColor, info.cell.Color, info.cell.Transparent, true, 0, false)
  1170. }
  1171. }
  1172. }
  1173. // Remember column infos.
  1174. t.visibleColumnIndices, t.visibleColumnWidths = columns, widths
  1175. }
  1176. // InputHandler returns the handler for this primitive.
  1177. func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
  1178. return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
  1179. key := event.Key()
  1180. if (!t.rowsSelectable && !t.columnsSelectable && key == tcell.KeyEnter) ||
  1181. key == tcell.KeyEscape ||
  1182. key == tcell.KeyTab ||
  1183. key == tcell.KeyBacktab {
  1184. if t.done != nil {
  1185. t.done(key)
  1186. }
  1187. return
  1188. }
  1189. // Movement functions.
  1190. previouslySelectedRow, previouslySelectedColumn := t.selectedRow, t.selectedColumn
  1191. lastColumn := t.content.GetColumnCount() - 1
  1192. rowCount := t.content.GetRowCount()
  1193. if rowCount == 0 {
  1194. return // No movement on empty tables.
  1195. }
  1196. var (
  1197. previous = func() {
  1198. startRow, previousRow := t.selectedRow, t.selectedRow
  1199. startColumn, previousColumn := t.selectedColumn, t.selectedColumn
  1200. for {
  1201. cell := t.content.GetCell(t.selectedRow, t.selectedColumn)
  1202. if cell != nil && !cell.NotSelectable {
  1203. return
  1204. }
  1205. t.selectedColumn--
  1206. if t.selectedColumn < 0 {
  1207. if t.wrapHorizontally {
  1208. t.selectedColumn = lastColumn
  1209. t.selectedRow--
  1210. if t.selectedRow < 0 {
  1211. if t.wrapVertically {
  1212. t.selectedRow = rowCount - 1
  1213. } else {
  1214. t.selectedRow = 0
  1215. }
  1216. }
  1217. } else {
  1218. t.selectedColumn = 0
  1219. }
  1220. }
  1221. if t.selectedColumn == startColumn && t.selectedRow == startRow {
  1222. t.selectedColumn = 0
  1223. t.selectedRow = 0
  1224. return
  1225. }
  1226. if t.selectedColumn == previousColumn && t.selectedRow == previousRow {
  1227. return
  1228. }
  1229. previousRow, previousColumn = t.selectedRow, t.selectedColumn
  1230. }
  1231. }
  1232. next = func() {
  1233. startRow, previousRow := t.selectedRow, t.selectedRow
  1234. startColumn, previousColumn := t.selectedColumn, t.selectedColumn
  1235. for {
  1236. if t.selectedColumn <= lastColumn {
  1237. cell := t.content.GetCell(t.selectedRow, t.selectedColumn)
  1238. if cell != nil && !cell.NotSelectable {
  1239. return
  1240. }
  1241. }
  1242. if t.selectedColumn < lastColumn {
  1243. t.selectedColumn++
  1244. } else {
  1245. if t.wrapHorizontally {
  1246. t.selectedColumn = 0
  1247. if t.selectedRow >= rowCount-1 {
  1248. if t.wrapVertically {
  1249. t.selectedRow = 0
  1250. } else {
  1251. t.selectedRow = rowCount - 1
  1252. }
  1253. } else {
  1254. t.selectedRow++
  1255. }
  1256. } else {
  1257. t.selectedColumn = lastColumn
  1258. }
  1259. }
  1260. if t.selectedColumn == startColumn && t.selectedRow == startRow {
  1261. t.selectedColumn = 0
  1262. t.selectedRow = 0
  1263. return
  1264. }
  1265. if t.selectedColumn == previousColumn && t.selectedRow == previousRow {
  1266. return
  1267. }
  1268. previousRow, previousColumn = t.selectedRow, t.selectedColumn
  1269. }
  1270. }
  1271. home = func() {
  1272. if t.rowsSelectable {
  1273. t.selectedRow = 0
  1274. t.selectedColumn = 0
  1275. t.clampToSelection = true
  1276. next()
  1277. } else {
  1278. t.trackEnd = false
  1279. t.rowOffset = 0
  1280. t.columnOffset = 0
  1281. }
  1282. }
  1283. end = func() {
  1284. if t.rowsSelectable {
  1285. t.selectedRow = rowCount - 1
  1286. t.selectedColumn = lastColumn
  1287. t.clampToSelection = true
  1288. previous()
  1289. } else {
  1290. t.trackEnd = true
  1291. t.columnOffset = 0
  1292. }
  1293. }
  1294. down = func() {
  1295. if t.rowsSelectable {
  1296. startRow := t.selectedRow
  1297. t.selectedRow++
  1298. if t.selectedRow >= rowCount {
  1299. t.selectedRow = 0
  1300. }
  1301. t.clampToSelection = true
  1302. next()
  1303. if !t.wrapVertically && t.selectedRow < startRow {
  1304. t.selectedRow = rowCount - 1
  1305. t.selectedColumn = lastColumn
  1306. previous()
  1307. }
  1308. } else {
  1309. t.rowOffset++
  1310. }
  1311. }
  1312. up = func() {
  1313. if t.rowsSelectable {
  1314. startRow := t.selectedRow
  1315. t.selectedRow--
  1316. if t.selectedRow < 0 {
  1317. t.selectedRow = rowCount - 1
  1318. }
  1319. t.clampToSelection = true
  1320. previous()
  1321. if !t.wrapVertically && t.selectedRow > startRow {
  1322. t.selectedRow = 0
  1323. t.selectedColumn = 0
  1324. next()
  1325. }
  1326. } else {
  1327. t.trackEnd = false
  1328. t.rowOffset--
  1329. }
  1330. }
  1331. left = func() {
  1332. if t.columnsSelectable {
  1333. startRow := t.selectedRow
  1334. startColumn := t.selectedColumn
  1335. t.selectedColumn--
  1336. if t.selectedColumn < 0 {
  1337. t.selectedColumn = lastColumn
  1338. t.selectedRow--
  1339. if t.selectedRow < 0 {
  1340. t.selectedRow = rowCount - 1
  1341. }
  1342. }
  1343. t.clampToSelection = true
  1344. previous()
  1345. if !t.wrapHorizontally && (t.selectedRow != startRow || t.selectedColumn > startColumn) ||
  1346. !t.wrapVertically && t.selectedRow > startRow {
  1347. t.selectedRow = startRow
  1348. t.selectedColumn = startColumn
  1349. }
  1350. } else {
  1351. t.columnOffset--
  1352. }
  1353. }
  1354. right = func() {
  1355. if t.columnsSelectable {
  1356. startRow := t.selectedRow
  1357. startColumn := t.selectedColumn
  1358. t.selectedColumn++
  1359. t.clampToSelection = true
  1360. next()
  1361. if !t.wrapHorizontally && (t.selectedRow != startRow || t.selectedColumn < startColumn) ||
  1362. !t.wrapVertically && t.selectedRow < startRow {
  1363. t.selectedRow = startRow
  1364. t.selectedColumn = startColumn
  1365. }
  1366. } else {
  1367. t.columnOffset++
  1368. }
  1369. }
  1370. pageDown = func() {
  1371. offsetAmount := t.visibleRows - t.fixedRows
  1372. if offsetAmount < 0 {
  1373. offsetAmount = 0
  1374. }
  1375. if t.rowsSelectable {
  1376. startRow := t.selectedRow
  1377. t.selectedRow += offsetAmount
  1378. if t.selectedRow >= rowCount {
  1379. t.selectedRow = rowCount - 1
  1380. }
  1381. t.clampToSelection = true
  1382. next()
  1383. if !t.wrapVertically && t.selectedRow < startRow {
  1384. t.selectedRow = rowCount - 1
  1385. t.selectedColumn = lastColumn
  1386. previous()
  1387. }
  1388. } else {
  1389. t.rowOffset += offsetAmount
  1390. }
  1391. }
  1392. pageUp = func() {
  1393. offsetAmount := t.visibleRows - t.fixedRows
  1394. if offsetAmount < 0 {
  1395. offsetAmount = 0
  1396. }
  1397. if t.rowsSelectable {
  1398. startRow := t.selectedRow
  1399. t.selectedRow -= offsetAmount
  1400. if t.selectedRow < 0 {
  1401. t.selectedRow = 0
  1402. }
  1403. t.clampToSelection = true
  1404. previous()
  1405. if !t.wrapVertically && t.selectedRow > startRow {
  1406. t.selectedRow = 0
  1407. t.selectedColumn = 0
  1408. next()
  1409. }
  1410. } else {
  1411. t.trackEnd = false
  1412. t.rowOffset -= offsetAmount
  1413. }
  1414. }
  1415. )
  1416. switch key {
  1417. case tcell.KeyRune:
  1418. switch event.Rune() {
  1419. case 'g':
  1420. home()
  1421. case 'G':
  1422. end()
  1423. case 'j':
  1424. down()
  1425. case 'k':
  1426. up()
  1427. case 'h':
  1428. left()
  1429. case 'l':
  1430. right()
  1431. }
  1432. case tcell.KeyHome:
  1433. home()
  1434. case tcell.KeyEnd:
  1435. end()
  1436. case tcell.KeyUp:
  1437. up()
  1438. case tcell.KeyDown:
  1439. down()
  1440. case tcell.KeyLeft:
  1441. left()
  1442. case tcell.KeyRight:
  1443. right()
  1444. case tcell.KeyPgDn, tcell.KeyCtrlF:
  1445. pageDown()
  1446. case tcell.KeyPgUp, tcell.KeyCtrlB:
  1447. pageUp()
  1448. case tcell.KeyEnter:
  1449. if (t.rowsSelectable || t.columnsSelectable) && t.selected != nil {
  1450. t.selected(t.selectedRow, t.selectedColumn)
  1451. }
  1452. }
  1453. // If the selection has changed, notify the handler.
  1454. if t.selectionChanged != nil &&
  1455. (t.rowsSelectable && previouslySelectedRow != t.selectedRow ||
  1456. t.columnsSelectable && previouslySelectedColumn != t.selectedColumn) {
  1457. t.selectionChanged(t.selectedRow, t.selectedColumn)
  1458. }
  1459. })
  1460. }
  1461. // MouseHandler returns the mouse handler for this primitive.
  1462. func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
  1463. return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
  1464. x, y := event.Position()
  1465. if !t.InRect(x, y) {
  1466. return false, nil
  1467. }
  1468. switch action {
  1469. case MouseLeftDown:
  1470. setFocus(t)
  1471. consumed = true
  1472. case MouseLeftClick:
  1473. selectEvent := true
  1474. row, column := t.cellAt(x, y)
  1475. cell := t.content.GetCell(row, column)
  1476. if cell != nil && cell.Clicked != nil {
  1477. if noSelect := cell.Clicked(); noSelect {
  1478. selectEvent = false
  1479. }
  1480. }
  1481. if selectEvent && (t.rowsSelectable || t.columnsSelectable) {
  1482. t.Select(row, column)
  1483. }
  1484. consumed = true
  1485. case MouseScrollUp:
  1486. t.trackEnd = false
  1487. t.rowOffset--
  1488. consumed = true
  1489. case MouseScrollDown:
  1490. t.rowOffset++
  1491. consumed = true
  1492. }
  1493. return
  1494. })
  1495. }