...

Source file src/github.com/rivo/tview/table.go

Documentation: github.com/rivo/tview

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

View as plain text