From d5edb975b6b0d1befe13e212a13294a8d9ba06d9 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Tue, 6 Oct 2020 18:19:40 -0700 Subject: [PATCH] Store ListItem text as []byte instead of string --- contextmenu.go | 2 +- demos/presentation/table.go | 6 ++-- demos/table/main.go | 4 +-- inputfield.go | 18 +++++----- list.go | 67 +++++++++++++++++++++++++------------ table.go | 6 ++-- table_test.go | 2 +- 7 files changed, 65 insertions(+), 40 deletions(-) diff --git a/contextmenu.go b/contextmenu.go index d7c83db..d619a77 100644 --- a/contextmenu.go +++ b/contextmenu.go @@ -154,7 +154,7 @@ func (c *ContextMenu) show(item int, x int, y int, setFocus func(Primitive)) { if c.selected != nil { c.l.Unlock() - c.selected(index, item.mainText, item.shortcut) + c.selected(index, string(item.mainText), item.shortcut) } else { c.l.Unlock() } diff --git a/demos/presentation/table.go b/demos/presentation/table.go index 78af2f8..91fb99d 100644 --- a/demos/presentation/table.go +++ b/demos/presentation/table.go @@ -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 bytes.Split([]byte(tableData), []byte("\n")) { - for column, cell := range bytes.Split(line, []byte("|")) { + for row, line := range strings.Split(tableData, "\n") { + for column, cell := range strings.Split(line, "|") { color := tcell.ColorWhite.TrueColor() if row == 0 { color = tcell.ColorYellow.TrueColor() diff --git a/demos/table/main.go b/demos/table/main.go index e17724e..da23b90 100644 --- a/demos/table/main.go +++ b/demos/table/main.go @@ -2,7 +2,7 @@ package main import ( - "bytes" + "strings" "github.com/gdamore/tcell/v2" "gitlab.com/tslocum/cview" @@ -14,7 +14,7 @@ func main() { app := cview.NewApplication() table := cview.NewTable(). SetBorders(true) - lorem := bytes.Split([]byte(loremIpsumText), []byte(" ")) + lorem := strings.Split(loremIpsumText, " ") cols, rows := 10, 40 word := 0 for r := 0; r < rows; r++ { diff --git a/inputfield.go b/inputfield.go index edeece7..fef7840 100644 --- a/inputfield.go +++ b/inputfield.go @@ -488,7 +488,7 @@ func (i *InputField) Autocomplete() *InputField { for index, entry := range entries { entry.enabled = true i.autocompleteList.AddItem(entry) - if currentEntry < 0 && entry.mainText == i.text { + if currentEntry < 0 && entry.GetMainText() == i.text { currentEntry = index } } @@ -505,10 +505,12 @@ func (i *InputField) Autocomplete() *InputField { // autocompleteChanged gets called when another item in the // autocomplete list has been selected. func (i *InputField) autocompleteChanged(_ int, item *ListItem) { - if len(item.secondaryText) > 0 && len(i.text) < len(item.secondaryText) { - i.autocompleteListSuggestion = item.secondaryText[len(i.text):] - } else if len(item.mainText) > len(i.text)+1 { - i.autocompleteListSuggestion = item.mainText[len(i.text)+1:] + mainText := item.GetMainText() + secondaryText := item.GetSecondaryText() + if len(secondaryText) > 0 && len(i.text) < len(secondaryText) { + i.autocompleteListSuggestion = secondaryText[len(i.text):] + } else if len(mainText) > len(i.text)+1 { + i.autocompleteListSuggestion = mainText[len(i.text)+1:] } else { i.autocompleteListSuggestion = "" } @@ -879,9 +881,9 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p case tcell.KeyEnter: // We might be done. if i.autocompleteList != nil { currentItem := i.autocompleteList.GetCurrentItem() - selectionText := currentItem.mainText - if currentItem.secondaryText != "" { - selectionText = currentItem.secondaryText + selectionText := currentItem.GetMainText() + if currentItem.GetSecondaryText() != "" { + selectionText = currentItem.GetSecondaryText() } i.Unlock() i.SetText(selectionText) diff --git a/list.go b/list.go index eafb4e2..a446c62 100644 --- a/list.go +++ b/list.go @@ -12,8 +12,8 @@ import ( // ListItem represents an item in a List. type ListItem struct { enabled bool // Whether or not the list item is selectable. - mainText string // The main text of the list item. - secondaryText string // A secondary text to be shown underneath the main text. + mainText []byte // The main text of the list item. + secondaryText []byte // A secondary text to be shown underneath the main text. shortcut rune // The key to select the list item directly, 0 if there is no shortcut. selected func() // The optional function which is called when the item is selected. reference interface{} // An optional reference object. @@ -24,13 +24,13 @@ type ListItem struct { // NewListItem returns a new item for a list. func NewListItem(mainText string) *ListItem { return &ListItem{ - mainText: mainText, + mainText: []byte(mainText), enabled: true, } } -// SetMainText sets the main text of the list item. -func (l *ListItem) SetMainText(val string) *ListItem { +// SetMainBytes sets the main text of the list item. +func (l *ListItem) SetMainBytes(val []byte) *ListItem { l.Lock() defer l.Unlock() @@ -38,16 +38,26 @@ func (l *ListItem) SetMainText(val string) *ListItem { return l } -// GetMainText returns the item's main text. -func (l *ListItem) GetMainText() string { +// SetMainText sets the main text of the list item. +func (l *ListItem) SetMainText(val string) *ListItem { + return l.SetMainBytes([]byte(val)) +} + +// GetMainBytes returns the item's main text. +func (l *ListItem) GetMainBytes() []byte { l.RLock() defer l.RUnlock() return l.mainText } -// SetSecondaryText sets a secondary text to be shown underneath the main text. -func (l *ListItem) SetSecondaryText(val string) *ListItem { +// GetMainText returns the item's main text. +func (l *ListItem) GetMainText() string { + return string(l.GetMainBytes()) +} + +// SetSecondaryBytes sets a secondary text to be shown underneath the main text. +func (l *ListItem) SetSecondaryBytes(val []byte) *ListItem { l.Lock() defer l.Unlock() @@ -55,14 +65,24 @@ func (l *ListItem) SetSecondaryText(val string) *ListItem { return l } -// GetSecondaryText returns the item's secondary text. -func (l *ListItem) GetSecondaryText() string { +// SetSecondaryText sets a secondary text to be shown underneath the main text. +func (l *ListItem) SetSecondaryText(val string) *ListItem { + return l.SetSecondaryBytes([]byte(val)) +} + +// GetSecondaryBytes returns the item's secondary text. +func (l *ListItem) GetSecondaryBytes() []byte { l.RLock() defer l.RUnlock() return l.secondaryText } +// GetSecondaryText returns the item's secondary text. +func (l *ListItem) GetSecondaryText() string { + return string(l.GetSecondaryBytes()) +} + // SetShortcut sets the key to select the ListItem directly, 0 if there is no shortcut. func (l *ListItem) SetShortcut(val rune) *ListItem { l.Lock() @@ -612,7 +632,7 @@ func (l *List) GetItemCount() int { func (l *List) GetItemText(index int) (main, secondary string) { l.RLock() defer l.RUnlock() - return l.items[index].mainText, l.items[index].secondaryText + return string(l.items[index].mainText), string(l.items[index].secondaryText) } // SetItemText sets an item's main and secondary text. Panics if the index is @@ -622,8 +642,8 @@ func (l *List) SetItemText(index int, main, secondary string) *List { defer l.Unlock() item := l.items[index] - item.mainText = main - item.secondaryText = secondary + item.mainText = []byte(main) + item.secondaryText = []byte(secondary) return l } @@ -661,19 +681,22 @@ func (l *List) FindItems(mainSearch, secondarySearch string, mustContainBoth, ig secondarySearch = strings.ToLower(secondarySearch) } + mainSearchBytes := []byte(mainSearch) + secondarySearchBytes := []byte(secondarySearch) + for index, item := range l.items { mainText := item.mainText secondaryText := item.secondaryText if ignoreCase { - mainText = strings.ToLower(mainText) - secondaryText = strings.ToLower(secondaryText) + mainText = bytes.ToLower(mainText) + secondaryText = bytes.ToLower(secondaryText) } // strings.Contains() always returns true for a "" search. - mainContained := strings.Contains(mainText, mainSearch) - secondaryContained := strings.Contains(secondaryText, secondarySearch) + mainContained := bytes.Contains(mainText, mainSearchBytes) + secondaryContained := bytes.Contains(secondaryText, secondarySearchBytes) if mustContainBoth && mainContained && secondaryContained || - !mustContainBoth && (mainText != "" && mainContained || secondaryText != "" && secondaryContained) { + !mustContainBoth && (len(mainText) > 0 && mainContained || len(secondaryText) > 0 && secondaryContained) { indices = append(indices, index) } } @@ -871,7 +894,7 @@ func (l *List) Draw(screen tcell.Screen) { break } - if item.mainText == "" && item.secondaryText == "" && item.shortcut == 0 { // Divider + if len(item.mainText) == 0 && len(item.secondaryText) == 0 && item.shortcut == 0 { // Divider Print(screen, []byte(string(tcell.RuneLTee)), (x-5)-l.paddingLeft, y, 1, AlignLeft, l.mainTextColor) Print(screen, bytes.Repeat([]byte(string(tcell.RuneHLine)), width+4+l.paddingLeft+l.paddingRight), (x-4)-l.paddingLeft, y, width+4+l.paddingLeft+l.paddingRight, AlignLeft, l.mainTextColor) Print(screen, []byte(string(tcell.RuneRTee)), (x-5)+width+5+l.paddingRight, y, 1, AlignLeft, l.mainTextColor) @@ -905,7 +928,7 @@ func (l *List) Draw(screen tcell.Screen) { if index == l.currentItem && (!l.selectedFocusOnly || hasFocus) { textWidth := width if !l.highlightFullLine { - if w := TaggedStringWidth(item.mainText); w < textWidth { + if w := TaggedTextWidth(item.mainText); w < textWidth { textWidth = w } } @@ -955,7 +978,7 @@ func (l *List) Draw(screen tcell.Screen) { // What's the longest option text? maxWidth := 0 for _, option := range ctx.items { - strWidth := TaggedStringWidth(option.mainText) + strWidth := TaggedTextWidth(option.mainText) if option.shortcut != 0 { strWidth += 4 } diff --git a/table.go b/table.go index 7a93d1d..1ff6099 100644 --- a/table.go +++ b/table.go @@ -53,9 +53,9 @@ 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 []byte) *TableCell { +func NewTableCell(text string) *TableCell { return &TableCell{ - Text: text, + Text: []byte(text), Align: AlignLeft, Color: Styles.PrimaryTextColor, BackgroundColor: tcell.ColorDefault, @@ -610,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 []byte) *Table { +func (t *Table) SetCellSimple(row, column int, text string) *Table { return t.SetCell(row, column, NewTableCell(text)) } diff --git a/table_test.go b/table_test.go index 9165e67..7661462 100644 --- a/table_test.go +++ b/table_test.go @@ -96,7 +96,7 @@ func tc(c *tableTestCase) *Table { 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.SetCellSimple(row, column, fmt.Sprintf("%d,%d", column, row)) } }