forked from tslocum/cview
Add Table test and benchmark, store TableCell as []byte instead of string
This commit is contained in:
parent
4cbfd55a8e
commit
96b2dd5523
|
@ -1,6 +1,7 @@
|
|||
v1.5.1 (WIP)
|
||||
- Store TextView buffer as [][]byte instead of []string
|
||||
- Add TextView.SetBytes and TextView.GetBytes
|
||||
- Add TableCell.SetBytes, TableCell.GetBytes and TableCell.GetText
|
||||
- Allow modification of scroll bar render text
|
||||
- Optimize TextView (writing is 90% faster, drawing is 50% faster)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"gitlab.com/tslocum/cview"
|
||||
|
@ -251,8 +251,8 @@ const tableSelectCell = `[green]func[white] [yellow]main[white]() {
|
|||
func Table(nextSlide func()) (title string, content cview.Primitive) {
|
||||
table := cview.NewTable().
|
||||
SetFixed(1, 1)
|
||||
for row, line := range strings.Split(tableData, "\n") {
|
||||
for column, cell := range strings.Split(line, "|") {
|
||||
for row, line := range bytes.Split([]byte(tableData), []byte("\n")) {
|
||||
for column, cell := range bytes.Split(line, []byte("|")) {
|
||||
color := tcell.ColorWhite.TrueColor()
|
||||
if row == 0 {
|
||||
color = tcell.ColorYellow.TrueColor()
|
||||
|
|
|
@ -2,17 +2,19 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"bytes"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
const loremIpsumText = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
|
||||
|
||||
func main() {
|
||||
app := cview.NewApplication()
|
||||
table := cview.NewTable().
|
||||
SetBorders(true)
|
||||
lorem := strings.Split("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", " ")
|
||||
lorem := bytes.Split([]byte(loremIpsumText), []byte(" "))
|
||||
cols, rows := 10, 40
|
||||
word := 0
|
||||
for r := 0; r < rows; r++ {
|
||||
|
|
45
table.go
45
table.go
|
@ -1,6 +1,7 @@
|
|||
package cview
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
|
@ -16,7 +17,7 @@ type TableCell struct {
|
|||
Reference interface{}
|
||||
|
||||
// The text to be displayed in the table cell.
|
||||
Text string
|
||||
Text []byte
|
||||
|
||||
// The alignment of the cell text. One of AlignLeft (default), AlignCenter,
|
||||
// or AlignRight.
|
||||
|
@ -52,7 +53,7 @@ type TableCell struct {
|
|||
// NewTableCell returns a new table cell with sensible defaults. That is, left
|
||||
// aligned text with the primary text color (see Styles) and a transparent
|
||||
// background (using the background of the Table).
|
||||
func NewTableCell(text string) *TableCell {
|
||||
func NewTableCell(text []byte) *TableCell {
|
||||
return &TableCell{
|
||||
Text: text,
|
||||
Align: AlignLeft,
|
||||
|
@ -61,8 +62,8 @@ func NewTableCell(text string) *TableCell {
|
|||
}
|
||||
}
|
||||
|
||||
// SetText sets the cell's text.
|
||||
func (c *TableCell) SetText(text string) *TableCell {
|
||||
// SetBytes sets the cell's text.
|
||||
func (c *TableCell) SetBytes(text []byte) *TableCell {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
|
@ -70,6 +71,24 @@ func (c *TableCell) SetText(text string) *TableCell {
|
|||
return c
|
||||
}
|
||||
|
||||
// SetText sets the cell's text.
|
||||
func (c *TableCell) SetText(text string) *TableCell {
|
||||
return c.SetBytes([]byte(text))
|
||||
}
|
||||
|
||||
// GetBytes returns the cell's text.
|
||||
func (c *TableCell) GetBytes() []byte {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
return c.Text
|
||||
}
|
||||
|
||||
// GetText returns the cell's text.
|
||||
func (c *TableCell) GetText() string {
|
||||
return string(c.GetBytes())
|
||||
}
|
||||
|
||||
// SetAlign sets the cell's text alignment, one of AlignLeft, AlignCenter, or
|
||||
// AlignRight.
|
||||
func (c *TableCell) SetAlign(align int) *TableCell {
|
||||
|
@ -285,7 +304,7 @@ type Table struct {
|
|||
// If set to true, the table's last row will always be visible.
|
||||
trackEnd bool
|
||||
|
||||
// The sort function of the table. Defaults to a case-sensitive string comparison.
|
||||
// The sort function of the table. Defaults to a case-sensitive comparison.
|
||||
sortFunc func(column, i, j int) bool
|
||||
|
||||
// Whether or not the table should be sorted when a fixed row is clicked.
|
||||
|
@ -591,7 +610,7 @@ func (t *Table) SetCell(row, column int, cell *TableCell) *Table {
|
|||
}
|
||||
|
||||
// SetCellSimple calls SetCell() with the given text, left-aligned, in white.
|
||||
func (t *Table) SetCellSimple(row, column int, text string) *Table {
|
||||
func (t *Table) SetCellSimple(row, column int, text []byte) *Table {
|
||||
return t.SetCell(row, column, NewTableCell(text))
|
||||
}
|
||||
|
||||
|
@ -797,7 +816,7 @@ func (t *Table) Sort(column int, descending bool) *Table {
|
|||
|
||||
if t.sortFunc == nil {
|
||||
t.sortFunc = func(column, i, j int) bool {
|
||||
return t.cells[i][column].Text < t.cells[j][column].Text
|
||||
return bytes.Compare(t.cells[i][column].Text, t.cells[j][column].Text) == -1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -995,7 +1014,7 @@ ColumnLoop:
|
|||
}
|
||||
for _, row := range evaluationRows {
|
||||
if cell := getCell(row, column); cell != nil {
|
||||
_, _, _, _, _, _, cellWidth := decomposeText([]byte(cell.Text), true, false)
|
||||
_, _, _, _, _, _, cellWidth := decomposeText(cell.Text, true, false)
|
||||
if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth {
|
||||
cellWidth = cell.MaxWidth
|
||||
}
|
||||
|
@ -1089,8 +1108,8 @@ ColumnLoop:
|
|||
finalWidth = width - columnX - 1
|
||||
}
|
||||
cell.x, cell.y, cell.width = x+columnX+1, y+rowY, finalWidth
|
||||
_, printed := printWithStyle(screen, []byte(cell.Text), x+columnX+1, y+rowY, finalWidth, cell.Align, SetAttributes(tcell.StyleDefault.Foreground(cell.Color), cell.Attributes))
|
||||
if TaggedStringWidth(cell.Text)-printed > 0 && printed > 0 {
|
||||
_, printed := printWithStyle(screen, cell.Text, x+columnX+1, y+rowY, finalWidth, cell.Align, SetAttributes(tcell.StyleDefault.Foreground(cell.Color), cell.Attributes))
|
||||
if TaggedTextWidth(cell.Text)-printed > 0 && printed > 0 {
|
||||
_, _, style, _ := screen.GetContent(x+columnX+finalWidth, y+rowY)
|
||||
printWithStyle(screen, []byte(string(SemigraphicsHorizontalEllipsis)), x+columnX+finalWidth, y+rowY, 1, AlignLeft, style)
|
||||
}
|
||||
|
@ -1201,7 +1220,7 @@ ColumnLoop:
|
|||
// the drawing of a cell by background color, selected cells last.
|
||||
type cellInfo struct {
|
||||
x, y, w, h int
|
||||
text tcell.Color
|
||||
color tcell.Color
|
||||
selected bool
|
||||
}
|
||||
cellsByBackgroundColor := make(map[tcell.Color][]*cellInfo)
|
||||
|
@ -1229,7 +1248,7 @@ ColumnLoop:
|
|||
y: by,
|
||||
w: bw,
|
||||
h: bh,
|
||||
text: cell.Color,
|
||||
color: cell.Color,
|
||||
selected: cellSelected,
|
||||
})
|
||||
if !ok {
|
||||
|
@ -1256,7 +1275,7 @@ ColumnLoop:
|
|||
if t.selectedStyle != tcell.StyleDefault {
|
||||
defer colorBackground(cell.x, cell.y, cell.w, cell.h, selBg, selFg, selAttr, false)
|
||||
} else {
|
||||
defer colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, cell.text, 0, true)
|
||||
defer colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, cell.color, 0, true)
|
||||
}
|
||||
} else {
|
||||
colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, tcell.ColorDefault, 0, false)
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package cview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var tableTestCases = generateTableTestCases()
|
||||
|
||||
type tableTestCase struct {
|
||||
rows int
|
||||
columns int
|
||||
fixedRows int
|
||||
fixedColumns int
|
||||
}
|
||||
|
||||
func (c *tableTestCase) String() string {
|
||||
return fmt.Sprintf("Rows=%d/Cols=%d/FixedRows=%d/FixedCols=%d", c.rows, c.columns, c.fixedRows, c.fixedColumns)
|
||||
}
|
||||
|
||||
func TestTable(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, c := range tableTestCases {
|
||||
c := c // Capture
|
||||
|
||||
t.Run(c.String(), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
table := tc(c)
|
||||
|
||||
app, err := newTestApp(table)
|
||||
if err != nil {
|
||||
t.Errorf("failed to initialize Application: %s", err)
|
||||
}
|
||||
|
||||
for row := 0; row < c.rows; row++ {
|
||||
for column := 0; column < c.columns; column++ {
|
||||
contents := table.GetCell(row, column).GetText()
|
||||
expected := fmt.Sprintf("%d,%d", column, row)
|
||||
if contents != expected {
|
||||
t.Errorf("failed to either get or set TableCell text: expected %s, got %s", expected, contents)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.Draw(app.screen)
|
||||
|
||||
table.Clear()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTableDraw(b *testing.B) {
|
||||
for _, c := range tableTestCases {
|
||||
c := c // Capture
|
||||
|
||||
b.Run(c.String(), func(b *testing.B) {
|
||||
table := tc(c)
|
||||
|
||||
app, err := newTestApp(table)
|
||||
if err != nil {
|
||||
b.Errorf("failed to initialize Application: %s", err)
|
||||
}
|
||||
|
||||
table.Draw(app.screen)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
table.Draw(app.screen)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateTableTestCases() []*tableTestCase {
|
||||
var cases []*tableTestCase
|
||||
for i := 1; i < 3; i++ {
|
||||
rows := i * 5
|
||||
for i := 1; i < 3; i++ {
|
||||
columns := i * 7
|
||||
for fixedRows := 0; fixedRows < 3; fixedRows++ {
|
||||
for fixedColumns := 0; fixedColumns < 3; fixedColumns++ {
|
||||
cases = append(cases, &tableTestCase{rows, columns, fixedRows, fixedColumns})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cases
|
||||
}
|
||||
|
||||
func tc(c *tableTestCase) *Table {
|
||||
table := NewTable()
|
||||
|
||||
for row := 0; row < c.rows; row++ {
|
||||
for column := 0; column < c.columns; column++ {
|
||||
table.SetCellSimple(row, column, []byte(fmt.Sprintf("%d,%d", column, row)))
|
||||
}
|
||||
}
|
||||
|
||||
table.SetFixed(c.fixedRows, c.fixedColumns)
|
||||
|
||||
return table
|
||||
}
|
Loading…
Reference in New Issue