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 selfmerge-requests/11/head
parent
22895b10bc
commit
d315a5c5b3
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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
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
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…
Reference in New Issue