nt

A sensible note-taking program
git clone git://git.laack.co/nt.git
Log | Files | Refs | README

table.go (52829B)


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