Browse Source

feat(List): Expose `ListItem`

BREAKING CHANGE:
- The signature of `SetChangedFunc` and `SetSelectedFunc` has changed to pass the newly exposed `ListItem` instead of its properties
- The signature of `AddItem` and `InsertItem` has changed to expect a `ListItem` instead of the properties them self
tablepad
Andreas Bieber 1 year ago
parent
commit
d315a5c5b3
  1. 10
      contextmenu.go
  2. 4
      demos/inputfield/autocomplete/main.go
  3. 8
      demos/inputfield/autocompleteasync/main.go
  4. 12
      demos/list/main.go
  5. 12
      demos/presentation/introduction.go
  6. 16
      demos/presentation/table.go
  7. 8
      dropdown.go
  8. 17
      inputfield.go
  9. 204
      list.go
  10. 16
      list_test.go

10
contextmenu.go

@ -60,11 +60,11 @@ func (c *ContextMenu) AddContextItem(text string, shortcut rune, selected func(i
c.initializeList()
c.list.AddItem(text, "", shortcut, c.wrap(selected))
c.list.AddItem(NewListItem(text).SetShortcut(shortcut).SetSelectedFunc(c.wrap(selected)))
if text == "" && shortcut == 0 {
c.list.Lock()
index := len(c.list.items) - 1
c.list.items[index].Enabled = false
c.list.items[index].enabled = false
c.list.Unlock()
}
@ -139,14 +139,14 @@ func (c *ContextMenu) show(item int, x int, y int, setFocus func(Primitive)) {
c.list.Lock()
for i, item := range c.list.items {
if item.Enabled {
if item.enabled {
c.list.currentItem = i
break
}
}
c.list.Unlock()
c.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) {
c.list.SetSelectedFunc(func(index int, item *ListItem) {
c.l.Lock()
// A context item was selected. Close the menu.
@ -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, mainText, shortcut)
c.selected(index, item.mainText, item.shortcut)
} else {
c.l.Unlock()
}

4
demos/inputfield/autocomplete/main.go

@ -19,13 +19,13 @@ func main() {
SetDoneFunc(func(key tcell.Key) {
app.Stop()
})
inputField.SetAutocompleteFunc(func(currentText string) (entries []string) {
inputField.SetAutocompleteFunc(func(currentText string) (entries []*cview.ListItem) {
if len(currentText) == 0 {
return
}
for _, word := range words {
if strings.HasPrefix(strings.ToLower(word), strings.ToLower(currentText)) {
entries = append(entries, word)
entries = append(entries, cview.NewListItem(word))
}
}
if len(entries) <= 1 {

8
demos/inputfield/autocompleteasync/main.go

@ -26,8 +26,8 @@ func main() {
// Set up autocomplete function.
var mutex sync.Mutex
prefixMap := make(map[string][]string)
inputField.SetAutocompleteFunc(func(currentText string) []string {
prefixMap := make(map[string][]*cview.ListItem)
inputField.SetAutocompleteFunc(func(currentText string) []*cview.ListItem {
// Ignore empty text.
prefix := strings.TrimSpace(strings.ToLower(currentText))
if prefix == "" {
@ -57,9 +57,9 @@ func main() {
if err := dec.Decode(&companies); err != nil {
return
}
entries := make([]string, 0, len(companies))
entries := make([]*cview.ListItem, 0, len(companies))
for _, c := range companies {
entries = append(entries, c.Name)
entries = append(entries, cview.NewListItem(c.Name))
}
mutex.Lock()
prefixMap[prefix] = entries

12
demos/list/main.go

@ -12,13 +12,13 @@ func main() {
reset := func() {
list.
Clear().
AddItem("List item 1", "Some explanatory text", 'a', nil).
AddItem("List item 2", "Some explanatory text", 'b', nil).
AddItem("List item 3", "Some explanatory text", 'c', nil).
AddItem("List item 4", "Some explanatory text", 'd', nil).
AddItem("Quit", "Press to exit", 'q', func() {
AddItem(cview.NewListItem("List item 1").SetSecondaryText("Some explanatory text").SetShortcut('a')).
AddItem(cview.NewListItem("List item 2").SetSecondaryText("Some explanatory text").SetShortcut('b')).
AddItem(cview.NewListItem("List item 3").SetSecondaryText("Some explanatory text").SetShortcut('c')).
AddItem(cview.NewListItem("List item 4").SetSecondaryText("Some explanatory text").SetShortcut('d')).
AddItem(cview.NewListItem("Quit").SetSecondaryText("Press to exit").SetShortcut('q').SetSelectedFunc(func() {
app.Stop()
})
}))
list.ContextMenuList().SetItemEnabled(3, false)
}

12
demos/presentation/introduction.go

@ -9,12 +9,12 @@ func Introduction(nextSlide func()) (title string, content cview.Primitive) {
reset := func() {
list.
Clear().
AddItem("A Go package for terminal based UIs", "with a special focus on rich interactive widgets", '1', nextSlide).
AddItem("Based on github.com/gdamore/tcell", "Like termbox but better (see tcell docs)", '2', nextSlide).
AddItem("Designed to be simple", `"Hello world" is 5 lines of code`, '3', nextSlide).
AddItem("Good for data entry", `For charts, use "termui" - for low-level views, use "gocui" - ...`, '4', nextSlide).
AddItem("Supports context menus", "Right click on one of these items or press Alt+Enter", '5', nextSlide).
AddItem("Extensive documentation", "Demo code is available for each widget", '6', nextSlide)
AddItem(cview.NewListItem("A Go package for terminal based UIs").SetSecondaryText("with a special focus on rich interactive widgets").SetShortcut('1').SetSelectedFunc(nextSlide)).
AddItem(cview.NewListItem("Based on github.com/gdamore/tcell").SetSecondaryText("Like termbox but better (see tcell docs)").SetShortcut('2').SetSelectedFunc(nextSlide)).
AddItem(cview.NewListItem("Designed to be simple").SetSecondaryText(`"Hello world" is 5 lines of code`).SetShortcut('3').SetSelectedFunc(nextSlide)).
AddItem(cview.NewListItem("Good for data entry").SetSecondaryText(`For charts, use "termui" - for low-level views, use "gocui" - ...`).SetShortcut('4').SetSelectedFunc(nextSlide)).
AddItem(cview.NewListItem("Supports context menus").SetSecondaryText("Right click on one of these items or press Alt+Enter").SetShortcut('5').SetSelectedFunc(nextSlide)).
AddItem(cview.NewListItem("Extensive documentation").SetSecondaryText("Demo code is available for each widget").SetShortcut('6').SetSelectedFunc(nextSlide))
list.ContextMenuList().SetItemEnabled(3, false)
}

16
demos/presentation/table.go

@ -341,14 +341,14 @@ func Table(nextSlide func()) (title string, content cview.Primitive) {
}
list.ShowSecondaryText(false).
AddItem("Basic table", "", 'b', basic).
AddItem("Table with separator", "", 's', separator).
AddItem("Table with borders", "", 'o', borders).
AddItem("Selectable rows", "", 'r', selectRow).
AddItem("Selectable columns", "", 'c', selectColumn).
AddItem("Selectable cells", "", 'l', selectCell).
AddItem("Navigate", "", 'n', navigate).
AddItem("Next slide", "", 'x', nextSlide)
AddItem(cview.NewListItem("Basic table").SetShortcut('b').SetSelectedFunc(basic)).
AddItem(cview.NewListItem("Table with separator").SetShortcut('s').SetSelectedFunc(separator)).
AddItem(cview.NewListItem("Table with borders").SetShortcut('o').SetSelectedFunc(borders)).
AddItem(cview.NewListItem("Selectable rows").SetShortcut('r').SetSelectedFunc(selectRow)).
AddItem(cview.NewListItem("Selectable columns").SetShortcut('c').SetSelectedFunc(selectColumn)).
AddItem(cview.NewListItem("Selectable cells").SetShortcut('l').SetSelectedFunc(selectCell)).
AddItem(cview.NewListItem("Navigate").SetShortcut('n').SetSelectedFunc(navigate)).
AddItem(cview.NewListItem("Next slide").SetShortcut('x').SetSelectedFunc(nextSlide))
list.SetBorderPadding(1, 1, 2, 2)
basic()

8
dropdown.go

@ -28,7 +28,7 @@ type DropDown struct {
// currently selected.
currentOption int
// Strings to be placed beefore and after the current option.
// Strings to be placed before and after the current option.
currentOptionPrefix, currentOptionSuffix string
// The text to be displayed when no option has yet been selected.
@ -370,7 +370,7 @@ func (d *DropDown) AddOption(text string, selected func()) *DropDown {
func (d *DropDown) addOption(text string, selected func()) *DropDown {
d.options = append(d.options, &dropDownOption{Text: text, Selected: selected})
d.list.AddItem(d.optionPrefix+text+d.optionSuffix, "", 0, nil)
d.list.AddItem(NewListItem(d.optionPrefix + text + d.optionSuffix))
return d
}
@ -507,7 +507,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
// Show the prefix.
currentOptionPrefixWidth := TaggedStringWidth(d.currentOptionPrefix)
prefixWidth := stringWidth(d.prefix)
listItemText := d.options[d.list.GetCurrentItem()].Text
listItemText := d.options[d.list.GetCurrentItemIndex()].Text
Print(screen, d.currentOptionPrefix, x, y, fieldWidth, AlignLeft, fieldTextColor)
Print(screen, d.prefix, x+currentOptionPrefixWidth, y, fieldWidth-currentOptionPrefixWidth, AlignLeft, d.prefixTextColor)
if len(d.prefix) < len(listItemText) {
@ -598,7 +598,7 @@ func (d *DropDown) openList(setFocus func(Primitive)) {
d.open = true
optionBefore := d.currentOption
d.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) {
d.list.SetSelectedFunc(func(index int, item *ListItem) {
if d.dragging {
return // If we're dragging the mouse, we don't want to trigger any events.
}

17
inputfield.go

@ -92,9 +92,9 @@ type InputField struct {
cursorPos int
// An optional autocomplete function which receives the current text of the
// input field and returns a slice of strings to be displayed in a drop-down
// input field and returns a slice of ListItems to be displayed in a drop-down
// selection.
autocomplete func(text string) []string
autocomplete func(text string) []*ListItem
// The List object which shows the selectable autocomplete entries. If not
// nil, the list's main texts represent the current autocomplete entries.
@ -369,12 +369,12 @@ func (i *InputField) SetMaskCharacter(mask rune) *InputField {
}
// SetAutocompleteFunc sets an autocomplete callback function which may return
// strings to be selected from a drop-down based on the current text of the
// ListItems to be selected from a drop-down based on the current text of the
// input field. The drop-down appears only if len(entries) > 0. The callback is
// invoked in this function and whenever the current text changes or when
// Autocomplete() is called. Entries are cleared when the user selects an entry
// or presses Escape.
func (i *InputField) SetAutocompleteFunc(callback func(currentText string) (entries []string)) *InputField {
func (i *InputField) SetAutocompleteFunc(callback func(currentText string) (entries []*ListItem)) *InputField {
i.Lock()
i.autocomplete = callback
i.Unlock()
@ -427,8 +427,9 @@ func (i *InputField) Autocomplete() *InputField {
currentEntry := -1
i.autocompleteList.Clear()
for index, entry := range entries {
i.autocompleteList.AddItem(entry, "", 0, nil)
if currentEntry < 0 && entry == i.text {
entry.enabled = true
i.autocompleteList.AddItem(entry)
if currentEntry < 0 && entry.mainText == i.text {
currentEntry = index
}
}
@ -778,7 +779,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
case tcell.KeyDown, tcell.KeyTab: // Autocomplete selection.
if i.autocompleteList != nil {
count := i.autocompleteList.GetItemCount()
newEntry := i.autocompleteList.GetCurrentItem() + 1
newEntry := i.autocompleteList.GetCurrentItemIndex() + 1
if newEntry >= count {
newEntry = 0
}
@ -793,7 +794,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
return
case tcell.KeyUp, tcell.KeyBacktab: // Autocomplete selection.
if i.autocompleteList != nil {
newEntry := i.autocompleteList.GetCurrentItem() - 1
newEntry := i.autocompleteList.GetCurrentItemIndex() - 1
if newEntry < 0 {
newEntry = i.autocompleteList.GetItemCount() - 1
}

204
list.go

@ -8,13 +8,60 @@ import (
"github.com/gdamore/tcell/v2"
)
// listItem represents one 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.
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.
// 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.
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.
}
// NewListItem returns a new item for the list.
func NewListItem(mainText string) *ListItem {
return &ListItem{
mainText: mainText,
enabled: true,
}
}
// SetMainText sets the main text of the list item.
func (l *ListItem) SetMainText(val string) *ListItem {
l.mainText = val
return l
}
// GetMainText returns the item's main text.
func (l *ListItem) GetMainText() string {
return l.mainText
}
// SetSecondaryText sets a secondary text to be shown underneath the main text.
func (l *ListItem) SetSecondaryText(val string) *ListItem {
l.secondaryText = val
return l
}
// GetSecondaryText returns the item's secondary text.
func (l *ListItem) GetSecondaryText() string {
return l.secondaryText
}
// SetShortcut sets the key to select the ListItem directly, 0 if there is no shortcut.
func (l *ListItem) SetShortcut(val rune) *ListItem {
l.shortcut = val
return l
}
// GetShortcut returns the ListItem's shortcut.
func (l *ListItem) GetShortcut() rune {
return l.shortcut
}
// SetSelectedFunc sets a function which is called when the ListItem is selected.
func (l *ListItem) SetSelectedFunc(handler func()) *ListItem {
l.selected = handler
return l
}
// List displays rows of items, each of which can be selected.
@ -23,7 +70,7 @@ type List struct {
*ContextMenu
// The items of the list.
items []*listItem
items []*ListItem
// The index of the currently selected item.
currentItem int
@ -78,11 +125,11 @@ type List struct {
// An optional function which is called when the user has navigated to a list
// item.
changed func(index int, mainText, secondaryText string, shortcut rune)
changed func(index int, item *ListItem)
// An optional function which is called when a list item was selected. This
// function will be called even if the list item defines its own callback.
selected func(index int, mainText, secondaryText string, shortcut rune)
selected func(index int, item *ListItem)
// An optional function which is called when the user presses the Escape key.
done func()
@ -140,7 +187,7 @@ func (l *List) SetCurrentItem(index int) *List {
if index != previousItem && l.changed != nil {
item := l.items[index]
l.Unlock()
l.changed(index, item.MainText, item.SecondaryText, item.Shortcut)
l.changed(index, item)
l.Lock()
}
@ -148,15 +195,33 @@ func (l *List) SetCurrentItem(index int) *List {
return l
}
// GetCurrentItem returns the index of the currently selected list item,
// starting at 0 for the first item.
func (l *List) GetCurrentItem() int {
// GetCurrentItem returns the currently selected list item,
// Returns nil if no item is selected.
func (l *List) GetCurrentItem() *ListItem {
l.RLock()
defer l.RUnlock()
if len(l.items) == 0 || l.currentItem >= len(l.items) {
return nil
}
return l.items[l.currentItem]
}
// GetCurrentItemIndex returns the index of the currently selected list item,
// starting at 0 for the first item and its struct.
func (l *List) GetCurrentItemIndex() int {
l.RLock()
defer l.RUnlock()
return l.currentItem
}
// GetItems returns all list items.
func (l *List) GetItems() []*ListItem {
l.RLock()
defer l.RUnlock()
return l.items
}
// RemoveItem removes the item with the given index (starting at 0) from the
// list. If a negative index is provided, items are referred to from the back
// (-1 = last item, -2 = second-to-last item, and so on). Out of range indices
@ -203,7 +268,7 @@ func (l *List) RemoveItem(index int) *List {
if previousItem == index && l.changed != nil {
item := l.items[l.currentItem]
l.Unlock()
l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
l.changed(l.currentItem, item)
} else {
l.Unlock()
}
@ -383,11 +448,11 @@ func (l *List) SetWrapAround(wrapAround bool) *List {
// SetChangedFunc sets the function which is called when the user navigates to
// a list item. The function receives the item's index in the list of items
// (starting with 0), its main text, secondary text, and its shortcut rune.
// (starting with 0) and the list item.
//
// This function is also called when the first item is added or when
// SetCurrentItem() is called.
func (l *List) SetChangedFunc(handler func(index int, mainText string, secondaryText string, shortcut rune)) *List {
func (l *List) SetChangedFunc(handler func(index int, item *ListItem)) *List {
l.Lock()
defer l.Unlock()
@ -397,9 +462,8 @@ func (l *List) SetChangedFunc(handler func(index int, mainText string, secondary
// SetSelectedFunc sets the function which is called when the user selects a
// list item by pressing Enter on the current selection. The function receives
// the item's index in the list of items (starting with 0), its main text,
// secondary text, and its shortcut rune.
func (l *List) SetSelectedFunc(handler func(int, string, string, rune)) *List {
// the item's index in the list of items (starting with 0) and its struct.
func (l *List) SetSelectedFunc(handler func(int, *ListItem)) *List {
l.Lock()
defer l.Unlock()
@ -418,8 +482,8 @@ func (l *List) SetDoneFunc(handler func()) *List {
}
// AddItem calls InsertItem() with an index of -1.
func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected func()) *List {
l.InsertItem(-1, mainText, secondaryText, shortcut, selected)
func (l *List) AddItem(item *ListItem) *List {
l.InsertItem(-1, item)
return l
}
@ -445,16 +509,10 @@ func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected f
// The currently selected item will shift its position accordingly. If the list
// was previously empty, a "changed" event is fired because the new item becomes
// selected.
func (l *List) InsertItem(index int, mainText, secondaryText string, shortcut rune, selected func()) *List {
func (l *List) InsertItem(index int, item *ListItem) *List {
l.Lock()
item := &listItem{
Enabled: true,
MainText: mainText,
SecondaryText: secondaryText,
Shortcut: shortcut,
Selected: selected,
}
item.enabled = true
// Shift index to range.
if index < 0 {
@ -482,7 +540,7 @@ func (l *List) InsertItem(index int, mainText, secondaryText string, shortcut ru
if len(l.items) == 1 && l.changed != nil {
item := l.items[0]
l.Unlock()
l.changed(0, item.MainText, item.SecondaryText, item.Shortcut)
l.changed(0, item)
} else {
l.Unlock()
}
@ -490,6 +548,15 @@ func (l *List) InsertItem(index int, mainText, secondaryText string, shortcut ru
return l
}
// GetItem returns the ListItem at the given index.
// Returns nil when index is out of bounds.
func (l *List) GetItem(index int) *ListItem {
if index > len(l.items)-1 {
return nil
}
return l.items[index]
}
// GetItemCount returns the number of items in the list.
func (l *List) GetItemCount() int {
l.RLock()
@ -503,8 +570,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 l.items[index].mainText, l.items[index].secondaryText
}
// SetItemText sets an item's main and secondary text. Panics if the index is
@ -514,8 +580,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 = main
item.secondaryText = secondary
return l
}
@ -526,7 +592,7 @@ func (l *List) SetItemEnabled(index int, enabled bool) *List {
defer l.Unlock()
item := l.items[index]
item.Enabled = enabled
item.enabled = enabled
return l
}
@ -554,8 +620,8 @@ func (l *List) FindItems(mainSearch, secondarySearch string, mustContainBoth, ig
}
for index, item := range l.items {
mainText := item.MainText
secondaryText := item.SecondaryText
mainText := item.mainText
secondaryText := item.secondaryText
if ignoreCase {
mainText = strings.ToLower(mainText)
secondaryText = strings.ToLower(secondaryText)
@ -660,7 +726,7 @@ func (l *List) transform(tr Transformation) {
}
item := l.items[l.currentItem]
if item.Enabled {
if item.enabled {
break
}
@ -738,7 +804,7 @@ func (l *List) Draw(screen tcell.Screen) {
// Do we show any shortcuts?
var showShortcuts bool
for _, item := range l.items {
if item.Shortcut != 0 {
if item.shortcut != 0 {
showShortcuts = true
x += 4
width -= 4
@ -761,7 +827,7 @@ func (l *List) Draw(screen tcell.Screen) {
break
}
if item.MainText == "" && item.SecondaryText == "" && item.Shortcut == 0 { // Divider
if item.mainText == "" && item.secondaryText == "" && item.shortcut == 0 { // Divider
Print(screen, string(tcell.RuneLTee), (x-5)-l.paddingLeft, y, 1, AlignLeft, l.mainTextColor)
Print(screen, strings.Repeat(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, string(tcell.RuneRTee), (x-5)+width+5+l.paddingRight, y, 1, AlignLeft, l.mainTextColor)
@ -769,14 +835,14 @@ func (l *List) Draw(screen tcell.Screen) {
RenderScrollBar(screen, l.scrollBarVisibility, scrollBarX, y, scrollBarHeight, len(l.items), l.currentItem, index-l.offset, l.hasFocus, l.scrollBarColor)
y++
continue
} else if !item.Enabled { // Disabled item
} else if !item.enabled { // Disabled item
// Shortcuts.
if showShortcuts && item.Shortcut != 0 {
Print(screen, fmt.Sprintf("(%s)", string(item.Shortcut)), x-5, y, 4, AlignRight, tcell.ColorDarkSlateGray.TrueColor())
if showShortcuts && item.shortcut != 0 {
Print(screen, fmt.Sprintf("(%s)", string(item.shortcut)), x-5, y, 4, AlignRight, tcell.ColorDarkSlateGray.TrueColor())
}
// Main text.
Print(screen, item.MainText, x, y, width, AlignLeft, tcell.ColorGray.TrueColor())
Print(screen, item.mainText, x, y, width, AlignLeft, tcell.ColorGray.TrueColor())
RenderScrollBar(screen, l.scrollBarVisibility, scrollBarX, y, scrollBarHeight, len(l.items), l.currentItem, index-l.offset, l.hasFocus, l.scrollBarColor)
y++
@ -784,18 +850,18 @@ func (l *List) Draw(screen tcell.Screen) {
}
// Shortcuts.
if showShortcuts && item.Shortcut != 0 {
Print(screen, fmt.Sprintf("(%s)", string(item.Shortcut)), x-5, y, 4, AlignRight, l.shortcutColor)
if showShortcuts && item.shortcut != 0 {
Print(screen, fmt.Sprintf("(%s)", string(item.shortcut)), x-5, y, 4, AlignRight, l.shortcutColor)
}
// Main text.
Print(screen, item.MainText, x, y, width, AlignLeft, l.mainTextColor)
Print(screen, item.mainText, x, y, width, AlignLeft, l.mainTextColor)
// Background color of selected text.
if index == l.currentItem && (!l.selectedFocusOnly || hasFocus) {
textWidth := width
if !l.highlightFullLine {
if w := TaggedStringWidth(item.MainText); w < textWidth {
if w := TaggedStringWidth(item.mainText); w < textWidth {
textWidth = w
}
}
@ -821,7 +887,7 @@ func (l *List) Draw(screen tcell.Screen) {
// Secondary text.
if l.showSecondaryText {
Print(screen, item.SecondaryText, x, y, width, AlignLeft, l.secondaryTextColor)
Print(screen, item.secondaryText, x, y, width, AlignLeft, l.secondaryTextColor)
RenderScrollBar(screen, l.scrollBarVisibility, scrollBarX, y, scrollBarHeight, len(l.items), l.currentItem, index-l.offset, l.hasFocus, l.scrollBarColor)
@ -845,8 +911,8 @@ func (l *List) Draw(screen tcell.Screen) {
// What's the longest option text?
maxWidth := 0
for _, option := range ctx.items {
strWidth := TaggedStringWidth(option.MainText)
if option.Shortcut != 0 {
strWidth := TaggedStringWidth(option.mainText)
if option.shortcut != 0 {
strWidth += 4
}
if strWidth > maxWidth {
@ -928,15 +994,15 @@ func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
} else if HitShortcut(event, Keys.Select, Keys.Select2) {
if l.currentItem >= 0 && l.currentItem < len(l.items) {
item := l.items[l.currentItem]
if item.Enabled {
if item.Selected != nil {
if item.enabled {
if item.selected != nil {
l.Unlock()
item.Selected()
item.selected()
l.Lock()
}
if l.selected != nil {
l.Unlock()
l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
l.selected(l.currentItem, item)
l.Lock()
}
}
@ -954,20 +1020,20 @@ func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
if ch != ' ' {
// It's not a space bar. Is it a shortcut?
for index, item := range l.items {
if item.Enabled && item.Shortcut == ch {
if item.enabled && item.shortcut == ch {
// We have a shortcut.
matchesShortcut = true
l.currentItem = index
item := l.items[l.currentItem]
if item.Selected != nil {
if item.selected != nil {
l.Unlock()
item.Selected()
item.selected()
l.Lock()
}
if l.selected != nil {
l.Unlock()
l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
l.selected(l.currentItem, item)
l.Lock()
}
@ -996,7 +1062,7 @@ func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
if l.currentItem != previousItem && l.currentItem < len(l.items) && l.changed != nil {
item := l.items[l.currentItem]
l.Unlock()
l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
l.changed(l.currentItem, item)
} else {
l.Unlock()
}
@ -1078,21 +1144,21 @@ func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
index := l.indexAtPoint(event.Position())
if index != -1 {
item := l.items[index]
if item.Enabled {
if item.enabled {
l.currentItem = index
if item.Selected != nil {
if item.selected != nil {
l.Unlock()
item.Selected()
item.selected()
l.Lock()
}
if l.selected != nil {
l.Unlock()
l.selected(index, item.MainText, item.SecondaryText, item.Shortcut)
l.selected(index, item)
l.Lock()
}
if index != l.currentItem && l.changed != nil {
l.Unlock()
l.changed(index, item.MainText, item.SecondaryText, item.Shortcut)
l.changed(index, item)
l.Lock()
}
}
@ -1116,11 +1182,11 @@ func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
index := l.indexAtPoint(event.Position())
if index != -1 {
item := l.items[index]
if item.Enabled {
if item.enabled {
l.currentItem = index
if index != l.currentItem && l.changed != nil {
l.Unlock()
l.changed(index, item.MainText, item.SecondaryText, item.Shortcut)
l.changed(index, item)
l.Lock()
}
}
@ -1135,7 +1201,7 @@ func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
index := l.indexAtY(y)
if index >= 0 {
item := l.items[index]
if item.Enabled {
if item.enabled {
l.currentItem = index
}
}

16
list_test.go

@ -18,17 +18,17 @@ func TestList(t *testing.T) {
l := NewList()
if l.GetItemCount() != 0 {
t.Errorf("failed to initialize List: expected item count 0, got %d", l.GetItemCount())
} else if l.GetCurrentItem() != 0 {
t.Errorf("failed to initialize List: expected current item 0, got %d", l.GetCurrentItem())
} else if l.GetCurrentItemIndex() != 0 {
t.Errorf("failed to initialize List: expected current item 0, got %d", l.GetCurrentItemIndex())
}
// Add item 0
l.AddItem(listTextA, listTextB, 'a', nil)
l.AddItem(NewListItem(listTextA).SetSecondaryText(listTextB).SetShortcut('a'))
if l.GetItemCount() != 1 {
t.Errorf("failed to update List: expected item count 1, got %d", l.GetItemCount())
} else if l.GetCurrentItem() != 0 {
t.Errorf("failed to update List: expected current item 0, got %d", l.GetCurrentItem())
} else if l.GetCurrentItemIndex() != 0 {
t.Errorf("failed to update List: expected current item 0, got %d", l.GetCurrentItemIndex())
}
// Get item 0 text
@ -42,11 +42,11 @@ func TestList(t *testing.T) {
// Add item 1
l.AddItem(listTextB, listTextC, 'a', nil)
l.AddItem(NewListItem(listTextB).SetSecondaryText(listTextC).SetShortcut('a'))
if l.GetItemCount() != 2 {
t.Errorf("failed to update List: expected item count 1, got %v", l.GetItemCount())
} else if l.GetCurrentItem() != 0 {
t.Errorf("failed to update List: expected current item 0, got %v", l.GetCurrentItem())
} else if l.GetCurrentItemIndex() != 0 {
t.Errorf("failed to update List: expected current item 0, got %v", l.GetCurrentItemIndex())
}
// Get item 1 text

Loading…
Cancel
Save