1
0
mirror of https://github.com/gizak/termui.git synced 2025-04-27 13:48:51 +08:00
termui/widgets/table.go

174 lines
5.4 KiB
Go
Raw Normal View History

2019-01-23 20:12:10 -08:00
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package widgets
import (
"image"
2021-05-12 07:25:37 +07:00
"strings"
2019-01-23 20:12:10 -08:00
2019-03-07 02:50:20 -08:00
. "github.com/gizak/termui/v3"
2019-01-23 20:12:10 -08:00
)
2019-01-24 06:21:07 -08:00
/*Table is like:
2019-01-23 20:12:10 -08:00
Awesome Table
Col0 | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
Some Item #1 | AAA | 123 | CCCCC | EEEEE | GGGGG | IIIII |
Some Item #2 | BBB | 456 | DDDDD | FFFFF | HHHHH | JJJJJ |
*/
type Table struct {
Block
2019-02-23 17:15:42 -08:00
Rows [][]string
ColumnWidths []int
TextStyle Style
2021-05-12 07:25:37 +07:00
TextWrap bool
2019-02-23 17:15:42 -08:00
RowSeparator bool
TextAlignment Alignment
RowStyles map[int]Style
FillRow bool
2019-02-28 18:36:03 -08:00
// ColumnResizer is called on each Draw. Can be used for custom column sizing.
ColumnResizer func()
2019-01-23 20:12:10 -08:00
}
func NewTable() *Table {
return &Table{
2019-02-28 18:36:03 -08:00
Block: *NewBlock(),
TextStyle: Theme.Table.Text,
RowSeparator: true,
RowStyles: make(map[int]Style),
ColumnResizer: func() {},
2019-01-23 20:12:10 -08:00
}
}
func (self *Table) Draw(buf *Buffer) {
self.Block.Draw(buf)
2019-02-28 18:36:03 -08:00
self.ColumnResizer()
2019-01-23 20:12:10 -08:00
columnWidths := self.ColumnWidths
if len(columnWidths) == 0 {
columnCount := len(self.Rows[0])
2019-02-23 17:15:42 -08:00
columnWidth := self.Inner.Dx() / columnCount
2019-01-23 20:12:10 -08:00
for i := 0; i < columnCount; i++ {
2019-02-23 17:15:42 -08:00
columnWidths = append(columnWidths, columnWidth)
2019-01-23 20:12:10 -08:00
}
}
yCoordinate := self.Inner.Min.Y
// draw rows
for i := 0; i < len(self.Rows) && yCoordinate < self.Inner.Max.Y; i++ {
row := self.Rows[i]
2021-05-12 07:25:37 +07:00
textWrap := false
2019-01-23 20:12:10 -08:00
colXCoordinate := self.Inner.Min.X
2019-02-01 18:12:22 +00:00
rowStyle := self.TextStyle
// get the row style if one exists
if style, ok := self.RowStyles[i]; ok {
rowStyle = style
}
if self.FillRow {
blankCell := NewCell(' ', rowStyle)
buf.Fill(blankCell, image.Rect(self.Inner.Min.X, yCoordinate, self.Inner.Max.X, yCoordinate+1))
}
2019-01-23 20:12:10 -08:00
// draw row cells
for j := 0; j < len(row); j++ {
2021-05-12 07:25:37 +07:00
if len(row[j]) > columnWidths[j] && self.TextWrap {
row = self.transformForTextWrap(row, i, j, columnWidths[j])
textWrap = true
}
2019-02-23 16:54:20 -08:00
col := ParseStyles(row[j], rowStyle)
2019-01-23 20:12:10 -08:00
// draw row cell
2019-02-23 17:15:42 -08:00
if len(col) > columnWidths[j] || self.TextAlignment == AlignLeft {
for _, cx := range BuildCellWithXArray(col) {
k, cell := cx.X, cx.Cell
2019-01-23 20:12:10 -08:00
if k == columnWidths[j] || colXCoordinate+k == self.Inner.Max.X {
2019-01-26 02:47:47 -08:00
cell.Rune = ELLIPSES
2019-01-23 20:12:10 -08:00
buf.SetCell(cell, image.Pt(colXCoordinate+k-1, yCoordinate))
break
} else {
buf.SetCell(cell, image.Pt(colXCoordinate+k, yCoordinate))
}
}
2019-02-23 17:15:42 -08:00
} else if self.TextAlignment == AlignCenter {
2019-01-23 20:12:10 -08:00
xCoordinateOffset := (columnWidths[j] - len(col)) / 2
stringXCoordinate := xCoordinateOffset + colXCoordinate
for _, cx := range BuildCellWithXArray(col) {
k, cell := cx.X, cx.Cell
2019-01-23 20:12:10 -08:00
buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate))
}
2019-02-23 17:15:42 -08:00
} else if self.TextAlignment == AlignRight {
2019-01-23 20:12:10 -08:00
stringXCoordinate := MinInt(colXCoordinate+columnWidths[j], self.Inner.Max.X) - len(col)
for _, cx := range BuildCellWithXArray(col) {
k, cell := cx.X, cx.Cell
2019-01-23 20:12:10 -08:00
buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate))
}
}
colXCoordinate += columnWidths[j] + 1
}
// draw vertical separators
2019-02-01 18:12:22 +00:00
separatorStyle := self.Block.BorderStyle
2019-01-23 20:12:10 -08:00
separatorXCoordinate := self.Inner.Min.X
2019-02-01 18:12:22 +00:00
verticalCell := NewCell(VERTICAL_LINE, separatorStyle)
for i, width := range columnWidths {
if self.FillRow && i < len(columnWidths)-1 {
verticalCell.Style.Bg = rowStyle.Bg
} else {
verticalCell.Style.Bg = self.Block.BorderStyle.Bg
}
2019-01-23 20:12:10 -08:00
separatorXCoordinate += width
buf.SetCell(verticalCell, image.Pt(separatorXCoordinate, yCoordinate))
separatorXCoordinate++
}
yCoordinate++
// draw horizontal separator
2021-05-12 07:25:37 +07:00
if textWrap {
continue
}
2019-02-01 18:12:22 +00:00
horizontalCell := NewCell(HORIZONTAL_LINE, separatorStyle)
2019-01-23 20:12:10 -08:00
if self.RowSeparator && yCoordinate < self.Inner.Max.Y && i != len(self.Rows)-1 {
buf.Fill(horizontalCell, image.Rect(self.Inner.Min.X, yCoordinate, self.Inner.Max.X, yCoordinate+1))
yCoordinate++
}
}
}
2021-05-12 07:25:37 +07:00
func (self *Table) transformForTextWrap(row []string, i, j, columnWidth int) []string {
2021-05-12 18:58:20 -07:00
// Split words by column width
2021-05-12 07:25:37 +07:00
words := strings.Split(row[j], " ")
newWords := []string{}
nextWords := []string{}
totalChar := 0
for i := 0; i < len(words); i++ {
word := words[i]
if totalChar+len(word)+1 > columnWidth {
nextWords = words[i:]
break
}
newWords = append(newWords, word)
totalChar += len(word) + 1
}
row[j] = strings.Join(newWords, " ")
2021-05-12 18:58:20 -07:00
// Insert new sentence
2021-05-12 07:25:37 +07:00
last := len(self.Rows) - 1
self.Rows = append(self.Rows, self.Rows[last])
copy(self.Rows[i+1:], self.Rows[i:last])
self.Rows[i+1] = make([]string, len(self.Rows[i]))
self.Rows[i+1][j] = strings.Join(nextWords, " ")
return row
}